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"