From d36bc76a05cce75d7504e9dab00226e8ba2ae087 Mon Sep 17 00:00:00 2001 From: Oliver Pinter Date: Mon, 8 Aug 2016 20:25:04 +0000 Subject: [PATCH] HBSD MFC: Handle races with listening socket close when connecting a unix socket. - by markj@ If the listening socket is closed while sonewconn() is executing, the nascent child socket is aborted, which results in recursion on the unp_link lock when the child's pru_detach method is invoked. Fix this by using a flag to mark such sockets, and skip a part of the socket's teardown during detach. Reported by: Raviprakash Darbha Tested by: pho MFC after: 2 weeks Differential Revision: https://reviews.freebsd.org/D7398 --8<-- [128] panic: __rw_wlock_hard: recursing but non-recursive rw unp_link_rwlock @ /usr/src/sys/kern/uipc_usrreq.c:654 [128] [128] cpuid = 1 [128] KDB: stack backtrace: [128] #0 0xffffffff8098dc90 at kdb_backtrace+0x60 [128] #1 0xffffffff80953ed6 at vpanic+0x126 [128] #2 0xffffffff80953da9 at kassert_panic+0x139 [128] #3 0xffffffff80951454 at __rw_wlock_hard+0x394 [128] #4 0xffffffff80951072 at _rw_wlock_cookie+0x92 [128] #5 0xffffffff809de636 at uipc_detach+0x36 [128] #6 0xffffffff809d217a at sofree+0x1da [128] #7 0xffffffff809d1da4 at sonewconn+0x324 [128] #8 0xffffffff809e0496 at unp_connectat+0x326 [128] #9 0xffffffff809de4ac at uipc_connect+0x4c [128] #10 0xffffffff809d8cf6 at kern_connectat+0x126 [128] #11 0xffffffff809d8b87 at sys_connect+0x77 [128] #12 0xffffffff80d6bab4 at amd64_syscall+0x2c4 [128] #13 0xffffffff80d4f6db at Xfast_syscall+0xfb [128] Uptime: 2m8s [128] Dumping 73 out of 487 MB:..22%..44%..66%..88% --8<-- (cherry picked from commit cfea0efd4a37a33ab3e4055bfe59ee6392277e6b) Signed-off-by: Oliver Pinter --- sys/kern/uipc_usrreq.c | 16 ++++++++++++++-- sys/sys/unpcb.h | 14 +++++++++----- 2 files changed, 23 insertions(+), 7 deletions(-) diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 123f0d4c02d2d0..efc4804a850d2b 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -430,6 +430,8 @@ uipc_attach(struct socket *so, int proto, struct thread *td) unp->unp_socket = so; so->so_pcb = unp; unp->unp_refcount = 1; + if (so->so_head != NULL) + unp->unp_flags |= UNP_NASCENT; UNP_LIST_LOCK(); unp->unp_gencnt = ++unp_gencnt; @@ -652,14 +654,22 @@ uipc_detach(struct socket *so) unp = sotounpcb(so); KASSERT(unp != NULL, ("uipc_detach: unp == NULL")); - UNP_LINK_WLOCK(); + vp = NULL; + local_unp_rights = 0; + UNP_LIST_LOCK(); - UNP_PCB_LOCK(unp); LIST_REMOVE(unp, unp_link); unp->unp_gencnt = ++unp_gencnt; --unp_count; UNP_LIST_UNLOCK(); + if ((unp->unp_flags & UNP_NASCENT) != 0) { + UNP_PCB_LOCK(unp); + goto teardown; + } + UNP_LINK_WLOCK(); + UNP_PCB_LOCK(unp); + /* * XXXRW: Should assert vp->v_socket == so. */ @@ -687,6 +697,7 @@ uipc_detach(struct socket *so) } local_unp_rights = unp_rights; UNP_LINK_WUNLOCK(); +teardown: unp->unp_socket->so_pcb = NULL; saved_unp_addr = unp->unp_addr; unp->unp_addr = NULL; @@ -1442,6 +1453,7 @@ unp_connect2(struct socket *so, struct socket *so2, int req) if (so2->so_type != so->so_type) return (EPROTOTYPE); + unp2->unp_flags &= ~UNP_NASCENT; unp->unp_conn = unp2; switch (so->so_type) { diff --git a/sys/sys/unpcb.h b/sys/sys/unpcb.h index 41fd5745bbd7a1..2547fdcbac85e7 100644 --- a/sys/sys/unpcb.h +++ b/sys/sys/unpcb.h @@ -103,11 +103,6 @@ struct unpcb { #define UNP_WANTCRED 0x004 /* credentials wanted */ #define UNP_CONNWAIT 0x008 /* connect blocks until accepted */ -#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 */ - /* * These flags are used to handle non-atomicity in connect() and bind() * operations on a socket: in particular, to avoid races between multiple @@ -115,6 +110,15 @@ struct unpcb { */ #define UNP_CONNECTING 0x010 /* Currently connecting. */ #define UNP_BINDING 0x020 /* Currently binding. */ +#define UNP_NASCENT 0x040 /* Newborn child socket. */ + +/* + * Flags in unp_gcflag. + */ +#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))