summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authormohans <mohans@FreeBSD.org>2006-11-22 23:54:29 +0000
committermohans <mohans@FreeBSD.org>2006-11-22 23:54:29 +0000
commit38630c101da43249a25319fdab637e7d0a57c5ca (patch)
tree618e6444337c3023afd2352f555940a29c9b202c /sys
parent0ccad3551bd4ca61a7073878dece214a4d10aac3 (diff)
downloadFreeBSD-src-38630c101da43249a25319fdab637e7d0a57c5ca.zip
FreeBSD-src-38630c101da43249a25319fdab637e7d0a57c5ca.tar.gz
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.
Diffstat (limited to 'sys')
-rw-r--r--sys/kern/uipc_socket.c48
1 files changed, 26 insertions, 22 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 635a2bf..f1690df 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"));
OpenPOWER on IntegriCloud