Author: markj
Date: Fri Sep  2 00:14:28 2016
New Revision: 305261
URL: https://svnweb.freebsd.org/changeset/base/305261

Log:
  MFC r285522:
  Fix cleanup race between unp_dispose and unp_gc.
  
  This change modifies the original commit to avoid changing the
  domain_dispose KPI.
  
  Tested by:    Oliver Pinter

Modified:
  stable/10/sys/kern/uipc_socket.c
  stable/10/sys/kern/uipc_usrreq.c
  stable/10/sys/sys/domain.h
  stable/10/sys/sys/unpcb.h
Directory Properties:
  stable/10/   (props changed)

Modified: stable/10/sys/kern/uipc_socket.c
==============================================================================
--- stable/10/sys/kern/uipc_socket.c    Thu Sep  1 23:58:36 2016        
(r305260)
+++ stable/10/sys/kern/uipc_socket.c    Fri Sep  2 00:14:28 2016        
(r305261)
@@ -741,8 +741,12 @@ sofree(struct socket *so)
        ACCEPT_UNLOCK();
 
        VNET_SO_ASSERT(so);
-       if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL)
-               (*pr->pr_domain->dom_dispose)(so->so_rcv.sb_mb);
+       if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) {
+               if (pr->pr_domain->dom_family == AF_LOCAL)
+                       unp_dispose_so(so);
+               else
+                       (*pr->pr_domain->dom_dispose)(so->so_rcv.sb_mb);
+       }
        if (pr->pr_usrreqs->pru_detach != NULL)
                (*pr->pr_usrreqs->pru_detach)(so);
 
@@ -2290,7 +2294,7 @@ sorflush(struct socket *so)
 {
        struct sockbuf *sb = &so->so_rcv;
        struct protosw *pr = so->so_proto;
-       struct sockbuf asb;
+       struct socket aso;
 
        VNET_SO_ASSERT(so);
 
@@ -2315,8 +2319,9 @@ sorflush(struct socket *so)
         * and mutex data unchanged.
         */
        SOCKBUF_LOCK(sb);
-       bzero(&asb, offsetof(struct sockbuf, sb_startzero));
-       bcopy(&sb->sb_startzero, &asb.sb_startzero,
+       bzero(&aso, sizeof(aso));
+       aso.so_pcb = so->so_pcb;
+       bcopy(&sb->sb_startzero, &aso.so_rcv.sb_startzero,
            sizeof(*sb) - offsetof(struct sockbuf, sb_startzero));
        bzero(&sb->sb_startzero,
            sizeof(*sb) - offsetof(struct sockbuf, sb_startzero));
@@ -2324,12 +2329,16 @@ sorflush(struct socket *so)
        sbunlock(sb);
 
        /*
-        * Dispose of special rights and flush the socket buffer.  Don't call
-        * any unsafe routines (that rely on locks being initialized) on asb.
+        * Dispose of special rights and flush the copied socket.  Don't call
+        * any unsafe routines (that rely on locks being initialized) on aso.
         */
-       if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL)
-               (*pr->pr_domain->dom_dispose)(asb.sb_mb);
-       sbrelease_internal(&asb, so);
+       if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) {
+               if (pr->pr_domain->dom_family == AF_LOCAL)
+                       unp_dispose_so(&aso);
+               else
+                       (*pr->pr_domain->dom_dispose)(aso.so_rcv.sb_mb);
+       }
+       sbrelease_internal(&aso.so_rcv, so);
 }
 
 /*

Modified: stable/10/sys/kern/uipc_usrreq.c
==============================================================================
--- stable/10/sys/kern/uipc_usrreq.c    Thu Sep  1 23:58:36 2016        
(r305260)
+++ stable/10/sys/kern/uipc_usrreq.c    Fri Sep  2 00:14:28 2016        
(r305261)
@@ -2200,15 +2200,19 @@ unp_gc_process(struct unpcb *unp)
         * Mark all sockets we reference with RIGHTS.
         */
        so = unp->unp_socket;
-       SOCKBUF_LOCK(&so->so_rcv);
-       unp_scan(so->so_rcv.sb_mb, unp_accessable);
-       SOCKBUF_UNLOCK(&so->so_rcv);
+       if ((unp->unp_gcflag & UNPGC_IGNORE_RIGHTS) == 0) {
+               SOCKBUF_LOCK(&so->so_rcv);
+               unp_scan(so->so_rcv.sb_mb, unp_accessable);
+               SOCKBUF_UNLOCK(&so->so_rcv);
+       }
 
        /*
         * Mark all sockets in our accept queue.
         */
        ACCEPT_LOCK();
        TAILQ_FOREACH(soa, &so->so_comp, so_list) {
+               if ((sotounpcb(soa)->unp_gcflag & UNPGC_IGNORE_RIGHTS) != 0)
+                       continue;
                SOCKBUF_LOCK(&soa->so_rcv);
                unp_scan(soa->so_rcv.sb_mb, unp_accessable);
                SOCKBUF_UNLOCK(&soa->so_rcv);
@@ -2238,11 +2242,13 @@ unp_gc(__unused void *arg, int pending)
        unp_taskcount++;
        UNP_LIST_LOCK();
        /*
-        * First clear all gc flags from previous runs.
+        * First clear all gc flags from previous runs, apart from
+        * UNPGC_IGNORE_RIGHTS.
         */
        for (head = heads; *head != NULL; head++)
                LIST_FOREACH(unp, *head, unp_link)
-                       unp->unp_gcflag = 0;
+                       unp->unp_gcflag =
+                           (unp->unp_gcflag & UNPGC_IGNORE_RIGHTS);
 
        /*
         * Scan marking all reachable sockets with UNPGC_REF.  Once a socket
@@ -2319,6 +2325,21 @@ unp_dispose(struct mbuf *m)
                unp_scan(m, unp_freerights);
 }
 
+/*
+ * Synchronize against unp_gc, which can trip over data as we are freeing it.
+ */
+void
+unp_dispose_so(struct socket *so)
+{
+       struct unpcb *unp;
+
+       unp = sotounpcb(so);
+       UNP_LIST_LOCK();
+       unp->unp_gcflag |= UNPGC_IGNORE_RIGHTS;
+       UNP_LIST_UNLOCK();
+       unp_dispose(so->so_rcv.sb_mb);
+}
+
 static void
 unp_scan(struct mbuf *m0, void (*op)(struct filedescent **, int))
 {

Modified: stable/10/sys/sys/domain.h
==============================================================================
--- stable/10/sys/sys/domain.h  Thu Sep  1 23:58:36 2016        (r305260)
+++ stable/10/sys/sys/domain.h  Fri Sep  2 00:14:28 2016        (r305261)
@@ -42,6 +42,7 @@
  */
 struct mbuf;
 struct ifnet;
+struct socket;
 
 struct domain {
        int     dom_family;             /* AF_xxx */
@@ -78,6 +79,9 @@ extern int    domain_init_status;
 extern struct  domain *domains;
 void           domain_add(void *);
 void           domain_init(void *);
+
+/* Hack to fix dom_dispose for unix domain sockets. */
+void           unp_dispose_so(struct socket *);
 #ifdef VIMAGE
 void           vnet_domain_init(void *);
 void           vnet_domain_uninit(void *);

Modified: stable/10/sys/sys/unpcb.h
==============================================================================
--- stable/10/sys/sys/unpcb.h   Thu Sep  1 23:58:36 2016        (r305260)
+++ stable/10/sys/sys/unpcb.h   Fri Sep  2 00:14:28 2016        (r305261)
@@ -118,6 +118,7 @@ struct unpcb {
 #define        UNPGC_REF                       0x1     /* unpcb has external 
ref. */
 #define        UNPGC_DEAD                      0x2     /* unpcb might be dead. 
*/
 #define        UNPGC_SCANNED                   0x4     /* Has been scanned. */
+#define        UNPGC_IGNORE_RIGHTS             0x8     /* Attached rights are 
freed */
 
 #define        sotounpcb(so)   ((struct unpcb *)((so)->so_pcb))
 
_______________________________________________
svn-src-stable-10@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-stable-10
To unsubscribe, send any mail to "svn-src-stable-10-unsubscr...@freebsd.org"

Reply via email to