Author: mmacy
Date: Thu May 24 21:13:46 2018
New Revision: 334185
URL: https://svnweb.freebsd.org/changeset/base/334185

Log:
  AF_UNIX: It is possible for UNIX datagram sockets to be connected
  to themselves. The updated code assumed that that could not happen
  and would try to lock the unp mutex twice.
  
  There may be a lingering issue here but this fixes it for the
  reporter.
  
  PR:   228458
  Reported by:  marieheleneka at gmail.com

Modified:
  head/sys/kern/uipc_usrreq.c

Modified: head/sys/kern/uipc_usrreq.c
==============================================================================
--- head/sys/kern/uipc_usrreq.c Thu May 24 21:11:38 2018        (r334184)
+++ head/sys/kern/uipc_usrreq.c Thu May 24 21:13:46 2018        (r334185)
@@ -722,7 +722,9 @@ uipc_close(struct socket *so)
        }
        unp2 = unp->unp_conn;
        unp_pcb_hold(unp);
-       if (unp2 != NULL) {
+       if (__predict_false(unp == unp2)) {
+               unp_disconnect(unp, unp2);
+       } else if (unp2 != NULL) {
                unp_pcb_hold(unp2);
                unp_pcb_owned_lock2(unp, unp2, freed);
                unp_disconnect(unp, unp2);
@@ -747,9 +749,13 @@ uipc_connect2(struct socket *so1, struct socket *so2)
        KASSERT(unp != NULL, ("uipc_connect2: unp == NULL"));
        unp2 = so2->so_pcb;
        KASSERT(unp2 != NULL, ("uipc_connect2: unp2 == NULL"));
-       unp_pcb_lock2(unp, unp2);
+       if (unp != unp2)
+               unp_pcb_lock2(unp, unp2);
+       else
+               UNP_PCB_LOCK(unp);
        error = unp_connect2(so1, so2, PRU_CONNECT2);
-       UNP_PCB_UNLOCK(unp2);
+       if (unp != unp2)
+               UNP_PCB_UNLOCK(unp2);
        UNP_PCB_UNLOCK(unp);
        return (error);
 }
@@ -783,29 +789,30 @@ uipc_detach(struct socket *so)
                mtx_lock(vplock);
        }
        UNP_PCB_LOCK(unp);
-       if ((unp2 = unp->unp_conn) != NULL) {
-               unp_pcb_owned_lock2(unp, unp2, freeunp);
-               if (freeunp)
-                       unp2 = NULL;
-       }
        if (unp->unp_vnode != vp &&
                unp->unp_vnode != NULL) {
                if (vplock)
                        mtx_unlock(vplock);
                UNP_PCB_UNLOCK(unp);
-               if (unp2)
-                       UNP_PCB_UNLOCK(unp2);
                goto restart;
        }
        if ((unp->unp_flags & UNP_NASCENT) != 0) {
-               if (unp2)
-                       UNP_PCB_UNLOCK(unp2);
                goto teardown;
        }
        if ((vp = unp->unp_vnode) != NULL) {
                VOP_UNP_DETACH(vp);
                unp->unp_vnode = NULL;
        }
+       if (__predict_false(unp == unp->unp_conn)) {
+               unp_disconnect(unp, unp);
+               unp2 = NULL;
+               goto connect_self;
+       }
+       if ((unp2 = unp->unp_conn) != NULL) {
+               unp_pcb_owned_lock2(unp, unp2, freeunp);
+               if (freeunp)
+                       unp2 = NULL;
+       }
        unp_pcb_hold(unp);
        if (unp2 != NULL) {
                unp_pcb_hold(unp2);
@@ -813,6 +820,7 @@ uipc_detach(struct socket *so)
                if (unp_pcb_rele(unp2) == 0)
                        UNP_PCB_UNLOCK(unp2);
        }
+ connect_self:
        UNP_PCB_UNLOCK(unp);
        UNP_REF_LIST_LOCK();
        while (!LIST_EMPTY(&unp->unp_refs)) {
@@ -864,6 +872,10 @@ uipc_disconnect(struct socket *so)
                UNP_PCB_UNLOCK(unp);
                return (0);
        }
+       if (unp == unp2) {
+               if (unp_pcb_rele(unp) == 0)
+                       UNP_PCB_UNLOCK(unp);
+       }
        unp_pcb_owned_lock2(unp, unp2, freed);
        if (__predict_false(freed)) {
                UNP_PCB_UNLOCK(unp);
@@ -1925,7 +1937,9 @@ unp_drop(struct unpcb *unp)
        if (so)
                so->so_error = ECONNRESET;
        unp2 = unp->unp_conn;
-       if (unp2 != NULL) {
+       if (unp2 == unp) {
+               unp_disconnect(unp, unp2);
+       } else if (unp2 != NULL) {
                unp_pcb_hold(unp2);
                unp_pcb_owned_lock2(unp, unp2, freed);
                unp_disconnect(unp, unp2);
_______________________________________________
[email protected] mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "[email protected]"

Reply via email to