From 84eab9ad73e26f63668454308a33ba60749912c2 Mon Sep 17 00:00:00 2001 From: Mohan Srinivasan Date: Wed, 22 Nov 2006 23:54:29 +0000 Subject: Fix a race in soclose() where connections could be queued to the listening socket after the pass that cleans those queues. This results in these connections being orphaned (and leaked). The fix is to clean up the so queues after detaching the socket from the protocol. Thanks to ups and jhb for discussions and a thorough code review. --- sys/kern/uipc_socket.c | 48 ++++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) (limited to 'sys/kern/uipc_socket.c') diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index 635a2bfcf350..f1690dfebe7e 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -592,6 +592,10 @@ sofree(so) (so->so_qstate & SQ_INCOMP) == 0, ("sofree: so_head == NULL, but still SQ_COMP(%d) or SQ_INCOMP(%d)", so->so_qstate & SQ_COMP, so->so_qstate & SQ_INCOMP)); + if (so->so_options & SO_ACCEPTCONN) { + KASSERT((TAILQ_EMPTY(&so->so_comp)), ("sofree: so_comp populated")); + KASSERT((TAILQ_EMPTY(&so->so_incomp)), ("sofree: so_comp populated")); + } SOCK_UNLOCK(so); ACCEPT_UNLOCK(); @@ -640,6 +644,28 @@ soclose(so) KASSERT(!(so->so_state & SS_NOFDREF), ("soclose: SS_NOFDREF on enter")); funsetown(&so->so_sigio); + if (so->so_state & SS_ISCONNECTED) { + if ((so->so_state & SS_ISDISCONNECTING) == 0) { + error = sodisconnect(so); + if (error) + goto drop; + } + if (so->so_options & SO_LINGER) { + if ((so->so_state & SS_ISDISCONNECTING) && + (so->so_state & SS_NBIO)) + goto drop; + while (so->so_state & SS_ISCONNECTED) { + error = tsleep(&so->so_timeo, + PSOCK | PCATCH, "soclos", so->so_linger * hz); + if (error) + break; + } + } + } + +drop: + if (so->so_proto->pr_usrreqs->pru_close != NULL) + (*so->so_proto->pr_usrreqs->pru_close)(so); if (so->so_options & SO_ACCEPTCONN) { struct socket *sp; ACCEPT_LOCK(); @@ -663,28 +689,6 @@ soclose(so) } ACCEPT_UNLOCK(); } - if (so->so_state & SS_ISCONNECTED) { - if ((so->so_state & SS_ISDISCONNECTING) == 0) { - error = sodisconnect(so); - if (error) - goto drop; - } - if (so->so_options & SO_LINGER) { - if ((so->so_state & SS_ISDISCONNECTING) && - (so->so_state & SS_NBIO)) - goto drop; - while (so->so_state & SS_ISCONNECTED) { - error = tsleep(&so->so_timeo, - PSOCK | PCATCH, "soclos", so->so_linger * hz); - if (error) - break; - } - } - } - -drop: - if (so->so_proto->pr_usrreqs->pru_close != NULL) - (*so->so_proto->pr_usrreqs->pru_close)(so); ACCEPT_LOCK(); SOCK_LOCK(so); KASSERT((so->so_state & SS_NOFDREF) == 0, ("soclose: NOFDREF")); -- cgit v1.2.3