Fix a NULL pointer dereference panic that occurs when the TCP protocol
authorMatthew Dillon <dillon@dragonflybsd.org>
Wed, 27 Oct 2004 03:43:47 +0000 (03:43 +0000)
committerMatthew Dillon <dillon@dragonflybsd.org>
Wed, 27 Oct 2004 03:43:47 +0000 (03:43 +0000)
stack races against userland while closing a tcp connection.  It is
possible for userland to queue a disconnect request but for the protocol
stack to then receive a packet that causes it to call tcp_drop()->tcp_close()
which also disconnects the inpcb from the tcpcb.  When the protocol stack
then processes the disconnect request it hits the panic because the inpcb
no longer has a tcpcb connected to it.

The bug generally only occured on SMP systems where the latency in intra-cpu
communication opens up the window of opportunity for the bug to occur.

Panic-Reported-by: Adam K Kirchhoff <adamk@voicenet.com>
sys/netinet/tcp_usrreq.c

index ca3de79..0004d3f 100644 (file)
@@ -82,7 +82,7 @@
  *
  *     From: @(#)tcp_usrreq.c  8.2 (Berkeley) 1/3/94
  * $FreeBSD: src/sys/netinet/tcp_usrreq.c,v 1.51.2.17 2002/10/11 11:46:44 ume Exp $
- * $DragonFly: src/sys/netinet/tcp_usrreq.c,v 1.26 2004/08/11 02:36:22 dillon Exp $
+ * $DragonFly: src/sys/netinet/tcp_usrreq.c,v 1.27 2004/10/27 03:43:47 dillon Exp $
  */
 
 #include "opt_ipsec.h"
@@ -215,15 +215,21 @@ tcp_usr_detach(struct socket *so)
        struct tcpcb *tp;
        TCPDEBUG0;
 
-       if (inp == 0) {
+       if (inp == NULL) {
                splx(s);
                return EINVAL;  /* XXX */
        }
-       tp = intotcpcb(inp);
-       TCPDEBUG1();
-       tp = tcp_disconnect(tp);
 
-       TCPDEBUG2(PRU_DETACH);
+       /*
+        * It's possible for the tcpcb (tp) to disconnect from the inp due
+        * to tcp_drop()->tcp_close() being called.  This may occur *after*
+        * the detach message has been queued so we may find a NULL tp here.
+        */
+       if ((tp = intotcpcb(inp)) != NULL) {
+               TCPDEBUG1();
+               tp = tcp_disconnect(tp);
+               TCPDEBUG2(PRU_DETACH);
+       }
        splx(s);
        return error;
 }