Skip to content

Commit

Permalink
HBSD MFC: Handle races with listening socket close when connecting a …
Browse files Browse the repository at this point in the history
…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 <[email protected]>
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] freebsd#9 0xffffffff809de4ac at uipc_connect+0x4c
[128] freebsd#10 0xffffffff809d8cf6 at kern_connectat+0x126
[128] freebsd#11 0xffffffff809d8b87 at sys_connect+0x77
[128] freebsd#12 0xffffffff80d6bab4 at amd64_syscall+0x2c4
[128] freebsd#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 cfea0ef)
Signed-off-by: Oliver Pinter <[email protected]>
  • Loading branch information
opntr committed Aug 8, 2016
1 parent e19f452 commit d36bc76
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 7 deletions.
16 changes: 14 additions & 2 deletions sys/kern/uipc_usrreq.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
*/
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down
14 changes: 9 additions & 5 deletions sys/sys/unpcb.h
Original file line number Diff line number Diff line change
Expand Up @@ -103,18 +103,22 @@ 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
* threads or processes operating simultaneously on the same socket.
*/
#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))

Expand Down

0 comments on commit d36bc76

Please sign in to comment.