Index: sys/kern/uipc_debug.c =================================================================== --- sys/kern/uipc_debug.c +++ sys/kern/uipc_debug.c @@ -248,6 +248,7 @@ db_printf("dom_init: %p ", d->dom_init); db_printf("dom_externalize: %p ", d->dom_externalize); db_printf("dom_dispose: %p\n", d->dom_dispose); + db_printf("dom_dispose2: %p\n", d->dom_dispose2); db_print_indent(indent); db_printf("dom_protosw: %p ", d->dom_protosw); Index: sys/kern/uipc_socket.c =================================================================== --- sys/kern/uipc_socket.c +++ sys/kern/uipc_socket.c @@ -804,8 +804,12 @@ 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) { + if (pr->pr_domain->dom_dispose2 != NULL) + (*pr->pr_domain->dom_dispose2)(so); + else if (pr->pr_domain->dom_dispose != NULL) + (*pr->pr_domain->dom_dispose)(so->so_rcv.sb_mb); + } if (pr->pr_usrreqs->pru_detach != NULL) (*pr->pr_usrreqs->pru_detach)(so); @@ -2393,8 +2397,12 @@ * Dispose of special rights and flush the socket buffer. Don't call * any unsafe routines (that rely on locks being initialized) on asb. */ - if (pr->pr_flags & PR_RIGHTS && pr->pr_domain->dom_dispose != NULL) - (*pr->pr_domain->dom_dispose)(asb.sb_mb); + if (pr->pr_flags & PR_RIGHTS) { + if (pr->pr_domain->dom_dispose2 != NULL) + (*pr->pr_domain->dom_dispose2)(so); + else if (pr->pr_domain->dom_dispose != NULL) + (*pr->pr_domain->dom_dispose)(so->so_rcv.sb_mb); + } sbrelease_internal(&asb, so); } Index: sys/kern/uipc_usrreq.c =================================================================== --- sys/kern/uipc_usrreq.c +++ sys/kern/uipc_usrreq.c @@ -278,6 +278,7 @@ static int unp_connect2(struct socket *so, struct socket *so2, int); static void unp_disconnect(struct unpcb *unp, struct unpcb *unp2); static void unp_dispose(struct mbuf *); +static void unp_dispose2(struct socket *so); static void unp_shutdown(struct unpcb *); static void unp_drop(struct unpcb *, int); static void unp_gc(__unused void *, int); @@ -334,7 +335,7 @@ .dom_name = "local", .dom_init = unp_init, .dom_externalize = unp_externalize, - .dom_dispose = unp_dispose, + .dom_dispose2 = unp_dispose2, .dom_protosw = localsw, .dom_protoswNPROTOSW = &localsw[sizeof(localsw)/sizeof(localsw[0])] }; @@ -2193,8 +2194,7 @@ struct socket *so; struct file *fp; - /* Already processed. */ - if (unp->unp_gcflag & UNPGC_SCANNED) + if (unp->unp_gcflag & (UNPGC_SCANNED | UNPGC_IGNORE)) return; fp = unp->unp_file; @@ -2252,11 +2252,11 @@ 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. */ for (head = heads; *head != NULL; head++) LIST_FOREACH(unp, *head, unp_link) - unp->unp_gcflag = 0; + unp->unp_gcflag = unp->unp_gcflag & UNPGC_IGNORE; /* * Scan marking all reachable sockets with UNPGC_REF. Once a socket @@ -2333,6 +2333,21 @@ unp_scan(m, unp_freerights); } +/* + * Synchronize against unp_gc, which can trip over data as we are freeing it. + */ +static void +unp_dispose2(struct socket *so) +{ + struct unpcb *unp; + + unp = sotounpcb(so); + UNP_LIST_LOCK(); + unp->unp_gcflag |= UNPGC_IGNORE; + UNP_LIST_UNLOCK(); + unp_dispose(so->so_rcv.sb_mb); +} + static void unp_scan(struct mbuf *m0, void (*op)(struct filedescent **, int)) { Index: sys/sys/domain.h =================================================================== --- sys/sys/domain.h +++ sys/sys/domain.h @@ -42,6 +42,7 @@ */ struct mbuf; struct ifnet; +struct socket; struct domain { int dom_family; /* AF_xxx */ @@ -54,6 +55,8 @@ (struct mbuf *, struct mbuf **, int); void (*dom_dispose) /* dispose of internalized rights */ (struct mbuf *); + void (*dom_dispose2) /* same, but takes socket */ + (struct socket *); struct protosw *dom_protosw, *dom_protoswNPROTOSW; struct domain *dom_next; int (*dom_rtattach) /* initialize routing table */ Index: sys/sys/unpcb.h =================================================================== --- sys/sys/unpcb.h +++ sys/sys/unpcb.h @@ -106,6 +106,7 @@ #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 0x4 /* Someone will clear it. */ /* * These flags are used to handle non-atomicity in connect() and bind()