diff options
author | Mohan Srinivasan <mohans@FreeBSD.org> | 2006-11-22 23:54:29 +0000 |
---|---|---|
committer | Mohan Srinivasan <mohans@FreeBSD.org> | 2006-11-22 23:54:29 +0000 |
commit | 84eab9ad73e26f63668454308a33ba60749912c2 (patch) | |
tree | 618e6444337c3023afd2352f555940a29c9b202c /sys/kern/uipc_socket.c | |
parent | a484c95a8d769ab787367742e36f5568cebf9bec (diff) | |
download | src-84eab9ad73e26f63668454308a33ba60749912c2.tar.gz src-84eab9ad73e26f63668454308a33ba60749912c2.zip |
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.
Notes
Notes:
svn path=/head/; revision=164530
Diffstat (limited to 'sys/kern/uipc_socket.c')
-rw-r--r-- | sys/kern/uipc_socket.c | 48 |
1 files changed, 26 insertions, 22 deletions
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")); |