summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTodd C. Miller <millert@cvs.openbsd.org>1999-01-21 03:27:44 +0000
committerTodd C. Miller <millert@cvs.openbsd.org>1999-01-21 03:27:44 +0000
commit1293acdffe00349f9103f9c93b5210c7c804743d (patch)
tree1b525d6e09e889138810de2f893af9998a0b68da
parent997abd3515c29b775ebbad1171951c329665b48a (diff)
Fixes select(2)/accept(2) race condition which permits DoS; mycroft@netbsd.org
-rw-r--r--sys/kern/uipc_socket.c33
-rw-r--r--sys/kern/uipc_socket2.c4
-rw-r--r--sys/sys/socketvar.h3
3 files changed, 24 insertions, 16 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c
index 0eedda1429f..17931e7a0b6 100644
--- a/sys/kern/uipc_socket.c
+++ b/sys/kern/uipc_socket.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_socket.c,v 1.22 1998/07/28 00:13:07 millert Exp $ */
+/* $OpenBSD: uipc_socket.c,v 1.23 1999/01/21 03:27:42 millert Exp $ */
/* $NetBSD: uipc_socket.c,v 1.21 1996/02/04 02:17:52 christos Exp $ */
/*
@@ -153,13 +153,13 @@ sofree(so)
register struct socket *so;
{
- if (so->so_pcb || (so->so_state & SS_NOFDREF) == 0)
+ /*
+ * We must not decommission a socket that's on the accept(2) queue.
+ * If we do, then accept(2) may hang even after select(2) indicated
+ * that the listening socket was ready.
+ */
+ if (so->so_pcb || so->so_head || (so->so_state & SS_NOFDREF) == 0)
return;
- if (so->so_head) {
- if (!soqremque(so, 0) && !soqremque(so, 1))
- panic("sofree dq");
- so->so_head = 0;
- }
sbrelease(&so->so_snd);
sorflush(so);
FREE(so, M_SOCKET);
@@ -174,14 +174,19 @@ int
soclose(so)
register struct socket *so;
{
+ struct socket *so2;
int s = splsoftnet(); /* conservative */
int error = 0;
if (so->so_options & SO_ACCEPTCONN) {
- while (so->so_q0)
- (void) soabort(so->so_q0);
- while (so->so_q)
- (void) soabort(so->so_q);
+ while ((so2 = so->so_q0) != NULL) {
+ (void) soqremque(so2, 0);
+ (void) soabort(so2);
+ }
+ while ((so2 = so->so_q) != NULL) {
+ (void) soqremque(so2, 1);
+ (void) soabort(so2);
+ }
}
if (so->so_pcb == 0)
goto discard;
@@ -237,12 +242,14 @@ soaccept(so, nam)
struct mbuf *nam;
{
int s = splsoftnet();
- int error;
+ int error = 0;
if ((so->so_state & SS_NOFDREF) == 0)
panic("soaccept: !NOFDREF");
so->so_state &= ~SS_NOFDREF;
- error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, NULL, nam, NULL);
+ if ((so->so_state & SS_ISDISCONNECTED) == 0)
+ error = (*so->so_proto->pr_usrreq)(so, PRU_ACCEPT, NULL,
+ nam, NULL);
splx(s);
return (error);
}
diff --git a/sys/kern/uipc_socket2.c b/sys/kern/uipc_socket2.c
index 4cbca6650f9..aa09a7ab148 100644
--- a/sys/kern/uipc_socket2.c
+++ b/sys/kern/uipc_socket2.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: uipc_socket2.c,v 1.7 1998/02/14 10:55:09 deraadt Exp $ */
+/* $OpenBSD: uipc_socket2.c,v 1.8 1999/01/21 03:27:42 millert Exp $ */
/* $NetBSD: uipc_socket2.c,v 1.11 1996/02/04 02:17:55 christos Exp $ */
/*
@@ -135,7 +135,7 @@ soisdisconnected(so)
{
so->so_state &= ~(SS_ISCONNECTING|SS_ISCONNECTED|SS_ISDISCONNECTING);
- so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE);
+ so->so_state |= (SS_CANTRCVMORE|SS_CANTSENDMORE|SS_ISDISCONNECTED);
wakeup((caddr_t)&so->so_timeo);
sowwakeup(so);
sorwakeup(so);
diff --git a/sys/sys/socketvar.h b/sys/sys/socketvar.h
index ccbdc74d0e1..a5613e8dd6f 100644
--- a/sys/sys/socketvar.h
+++ b/sys/sys/socketvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: socketvar.h,v 1.12 1998/02/14 10:55:08 deraadt Exp $ */
+/* $OpenBSD: socketvar.h,v 1.13 1999/01/21 03:27:43 millert Exp $ */
/* $NetBSD: socketvar.h,v 1.18 1996/02/09 18:25:38 christos Exp $ */
/*-
@@ -113,6 +113,7 @@ struct socket {
#define SS_CANTSENDMORE 0x010 /* can't send more data to peer */
#define SS_CANTRCVMORE 0x020 /* can't receive more data from peer */
#define SS_RCVATMARK 0x040 /* at mark on input */
+#define SS_ISDISCONNECTED 0x800 /* socket disconnected from peer */
#define SS_PRIV 0x080 /* privileged for broadcast, raw... */
#define SS_NBIO 0x100 /* non-blocking ops */