net/uip: Avoid deadlock in uip_tcp_socket_free()
The function uip_tcp_socket_free() is called with the sk lock held, but
then goes on to call uip_tcp_socket_close() which attempts to aquire the
lock a second time, triggering a deadlock if there are outstanding TCP
connections.
Rename the existing uip_tcp_socket_close() to a _locked variety and
removing the locking from it. Add a new uip_tcp_socket_close() which
takes the lock and calls the _locked variety.
Fixes: d87b503f4d6e ("net/uip: Add exit function")
Signed-off-by: Steven Price <steven.price@arm.com>
Signed-off-by: Will Deacon <will@kernel.org>
diff --git a/net/uip/tcp.c b/net/uip/tcp.c
index 8e0ad52..42e6e99 100644
--- a/net/uip/tcp.c
+++ b/net/uip/tcp.c
@@ -6,7 +6,7 @@
#include <linux/list.h>
#include <arpa/inet.h>
-static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
+static int uip_tcp_socket_close_locked(struct uip_tcp_socket *sk, int how)
{
shutdown(sk->fd, how);
@@ -14,9 +14,7 @@
shutdown(sk->fd, SHUT_RDWR);
close(sk->fd);
- mutex_lock(sk->lock);
list_del(&sk->list);
- mutex_unlock(sk->lock);
free(sk->buf);
free(sk);
@@ -25,6 +23,17 @@
return 0;
}
+static int uip_tcp_socket_close(struct uip_tcp_socket *sk, int how)
+{
+ int ret;
+
+ mutex_lock(sk->lock);
+ ret = uip_tcp_socket_close_locked(sk, how);
+ mutex_unlock(sk->lock);
+
+ return ret;
+}
+
static struct uip_tcp_socket *uip_tcp_socket_find(struct uip_tx_arg *arg, u32 sip, u32 dip, u16 sport, u16 dport)
{
struct list_head *sk_head;
@@ -110,7 +119,7 @@
}
sk->write_done = sk->read_done = 1;
- uip_tcp_socket_close(sk, SHUT_RDWR);
+ uip_tcp_socket_close_locked(sk, SHUT_RDWR);
}
static int uip_tcp_payload_send(struct uip_tcp_socket *sk, u8 flag, u16 payload_len)