Skip to content

Commit

Permalink
Handle races with listening socket close when connecting a unix socket.
Browse files Browse the repository at this point in the history
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
  • Loading branch information
markjdb committed Aug 8, 2016
1 parent 7602bc0 commit cfea0ef
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 @@ -1473,6 +1484,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 cfea0ef

Please sign in to comment.