summaryrefslogtreecommitdiff
path: root/sys/kern
diff options
context:
space:
mode:
authorPhilip Guenthe <guenther@cvs.openbsd.org>2012-10-17 05:07:56 +0000
committerPhilip Guenthe <guenther@cvs.openbsd.org>2012-10-17 05:07:56 +0000
commit9338a56e6d0f1ca6861fe7439b48f45fbb7cb44a (patch)
tree4cf27452088c626416c6be7badd5b063de276ff1 /sys/kern
parent700ad4fc04948c9012504f7838d520923e169212 (diff)
In sys_accept(), don't sleep between pulling the new socket from the
queue and calling soaccept(), so that the socket can't get torn down by a TCP RST in the middle and trigger "panic: soaccept: !NOFDREF", as seen by halex@ Analysis, original diff, and ok bluhm@
Diffstat (limited to 'sys/kern')
-rw-r--r--sys/kern/uipc_syscalls.c47
1 files changed, 28 insertions, 19 deletions
diff --git a/sys/kern/uipc_syscalls.c b/sys/kern/uipc_syscalls.c
index a1407a71d46..1eb096cebd2 100644
--- a/sys/kern/uipc_syscalls.c
+++ b/sys/kern/uipc_syscalls.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_syscalls.c,v 1.86 2012/05/06 20:25:27 matthew Exp $ */
+/* $OpenBSD: uipc_syscalls.c,v 1.87 2012/10/17 05:07:55 guenther Exp $ */
/* $NetBSD: uipc_syscalls.c,v 1.19 1996/02/09 19:00:48 christos Exp $ */
/*
@@ -172,6 +172,7 @@ sys_accept(struct proc *p, void *v, register_t *retval)
headfp = fp;
s = splsoftnet();
head = fp->f_data;
+redo:
if ((head->so_options & SO_ACCEPTCONN) == 0) {
error = EINVAL;
goto bad;
@@ -199,35 +200,44 @@ sys_accept(struct proc *p, void *v, register_t *retval)
goto bad;
}
- /*
- * At this point we know that there is at least one connection
- * ready to be accepted. Remove it from the queue prior to
- * allocating the file descriptor for it since falloc() may
- * block allowing another process to accept the connection
- * instead.
- */
- so = TAILQ_FIRST(&head->so_q);
- if (soqremque(so, 1) == 0)
- panic("accept");
-
/* Take note if socket was non-blocking. */
- nflag = (fp->f_flag & FNONBLOCK);
+ nflag = (headfp->f_flag & FNONBLOCK);
fdplock(p->p_fd);
error = falloc(p, &fp, &tmpfd);
fdpunlock(p->p_fd);
if (error != 0) {
/*
- * Probably ran out of file descriptors. Put the
- * unaccepted connection back onto the queue and
- * do another wakeup so some other process might
- * have a chance at it.
+ * Probably ran out of file descriptors. Wakeup
+ * so some other process might have a chance at it.
*/
- soqinsque(head, so, 1);
wakeup_one(&head->so_timeo);
goto bad;
}
+ nam = m_get(M_WAIT, MT_SONAME);
+
+ /*
+ * Check whether the queue emptied while we slept: falloc() or
+ * m_get() may have blocked, allowing the connection to be reset
+ * or another thread or process to accept it. If so, start over.
+ */
+ if (head->so_qlen == 0) {
+ m_freem(nam);
+ fdplock(p->p_fd);
+ fdremove(p->p_fd, tmpfd);
+ closef(fp, p);
+ fdpunlock(p->p_fd);
+ goto redo;
+ }
+
+ /*
+ * Do not sleep after we have taken the socket out of the queue.
+ */
+ so = TAILQ_FIRST(&head->so_q);
+ if (soqremque(so, 1) == 0)
+ panic("accept");
+
/* connection has been removed from the listen queue */
KNOTE(&head->so_rcv.sb_sel.si_note, 0);
@@ -235,7 +245,6 @@ sys_accept(struct proc *p, void *v, register_t *retval)
fp->f_flag = FREAD | FWRITE | nflag;
fp->f_ops = &socketops;
fp->f_data = so;
- nam = m_get(M_WAIT, MT_SONAME);
error = soaccept(so, nam);
if (!error && SCARG(uap, name)) {
error = copyaddrout(p, nam, SCARG(uap, name), namelen,