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
This commit is contained in:
parent
a79616dbee
commit
a80af232a4
@ -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);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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))
|
||||
{
|
||||
|
@ -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 *);
|
||||
|
@ -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))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user