aboutsummaryrefslogtreecommitdiff
path: root/sys/kern/uipc_socket.c
diff options
context:
space:
mode:
authorMohan Srinivasan <mohans@FreeBSD.org>2006-11-22 23:54:29 +0000
committerMohan Srinivasan <mohans@FreeBSD.org>2006-11-22 23:54:29 +0000
commit84eab9ad73e26f63668454308a33ba60749912c2 (patch)
tree618e6444337c3023afd2352f555940a29c9b202c /sys/kern/uipc_socket.c
parenta484c95a8d769ab787367742e36f5568cebf9bec (diff)
downloadsrc-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.c48
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"));