diff options
author | cem <cem@FreeBSD.org> | 2015-07-14 02:00:50 +0000 |
---|---|---|
committer | cem <cem@FreeBSD.org> | 2015-07-14 02:00:50 +0000 |
commit | 576619e564618bca3675db57580d8e1f76bd2ac7 (patch) | |
tree | 739399a2fae275099b377fb3967997e0a672d3b6 /sys/kern/uipc_socket.c | |
parent | 05a0acba04631454bc202315fdcc3a3cd8119838 (diff) | |
download | FreeBSD-src-576619e564618bca3675db57580d8e1f76bd2ac7.zip FreeBSD-src-576619e564618bca3675db57580d8e1f76bd2ac7.tar.gz |
Fix cleanup race between unp_dispose and unp_gc
unp_dispose and unp_gc could race to teardown the same mbuf chains, which
can lead to dereferencing freed filedesc pointers.
This patch adds an IGNORE_RIGHTS flag on unpcbs marking the unpcb's RIGHTS
as invalid/freed. The flag is protected by UNP_LIST_LOCK.
To serialize against unp_gc, unp_dispose needs the socket object. Change the
dom_dispose() KPI to take a socket object instead of an mbuf chain directly.
PR: 194264
Differential Revision: https://reviews.freebsd.org/D3044
Reviewed by: mjg (earlier version)
Approved by: markj (mentor)
Obtained from: mjg
MFC after: 1 month
Sponsored by: EMC / Isilon Storage Division
Diffstat (limited to 'sys/kern/uipc_socket.c')
-rw-r--r-- | sys/kern/uipc_socket.c | 17 |
1 files changed, 9 insertions, 8 deletions
diff --git a/sys/kern/uipc_socket.c b/sys/kern/uipc_socket.c index a431b4b..c9d8179 100644 --- a/sys/kern/uipc_socket.c +++ b/sys/kern/uipc_socket.c @@ -805,7 +805,7 @@ sofree(struct socket *so) 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); + (*pr->pr_domain->dom_dispose)(so); if (pr->pr_usrreqs->pru_detach != NULL) (*pr->pr_usrreqs->pru_detach)(so); @@ -2356,7 +2356,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); @@ -2381,8 +2381,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)); @@ -2390,12 +2391,12 @@ 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); + (*pr->pr_domain->dom_dispose)(&aso); + sbrelease_internal(&aso.so_rcv, so); } /* |