AF_UNIX: It is possible for UNIX datagram sockets to be connected

to themselves. The updated code assumed that that could not happen
and would try to lock the unp mutex twice.

There may be a lingering issue here but this fixes it for the
reporter.

PR:	228458
Reported by:	marieheleneka at gmail.com
This commit is contained in:
Matt Macy 2018-05-24 21:13:46 +00:00
parent 509b94387a
commit acf9fd05d8

View File

@ -722,7 +722,9 @@ uipc_close(struct socket *so)
}
unp2 = unp->unp_conn;
unp_pcb_hold(unp);
if (unp2 != NULL) {
if (__predict_false(unp == unp2)) {
unp_disconnect(unp, unp2);
} else if (unp2 != NULL) {
unp_pcb_hold(unp2);
unp_pcb_owned_lock2(unp, unp2, freed);
unp_disconnect(unp, unp2);
@ -747,9 +749,13 @@ uipc_connect2(struct socket *so1, struct socket *so2)
KASSERT(unp != NULL, ("uipc_connect2: unp == NULL"));
unp2 = so2->so_pcb;
KASSERT(unp2 != NULL, ("uipc_connect2: unp2 == NULL"));
unp_pcb_lock2(unp, unp2);
if (unp != unp2)
unp_pcb_lock2(unp, unp2);
else
UNP_PCB_LOCK(unp);
error = unp_connect2(so1, so2, PRU_CONNECT2);
UNP_PCB_UNLOCK(unp2);
if (unp != unp2)
UNP_PCB_UNLOCK(unp2);
UNP_PCB_UNLOCK(unp);
return (error);
}
@ -783,29 +789,30 @@ uipc_detach(struct socket *so)
mtx_lock(vplock);
}
UNP_PCB_LOCK(unp);
if ((unp2 = unp->unp_conn) != NULL) {
unp_pcb_owned_lock2(unp, unp2, freeunp);
if (freeunp)
unp2 = NULL;
}
if (unp->unp_vnode != vp &&
unp->unp_vnode != NULL) {
if (vplock)
mtx_unlock(vplock);
UNP_PCB_UNLOCK(unp);
if (unp2)
UNP_PCB_UNLOCK(unp2);
goto restart;
}
if ((unp->unp_flags & UNP_NASCENT) != 0) {
if (unp2)
UNP_PCB_UNLOCK(unp2);
goto teardown;
}
if ((vp = unp->unp_vnode) != NULL) {
VOP_UNP_DETACH(vp);
unp->unp_vnode = NULL;
}
if (__predict_false(unp == unp->unp_conn)) {
unp_disconnect(unp, unp);
unp2 = NULL;
goto connect_self;
}
if ((unp2 = unp->unp_conn) != NULL) {
unp_pcb_owned_lock2(unp, unp2, freeunp);
if (freeunp)
unp2 = NULL;
}
unp_pcb_hold(unp);
if (unp2 != NULL) {
unp_pcb_hold(unp2);
@ -813,6 +820,7 @@ uipc_detach(struct socket *so)
if (unp_pcb_rele(unp2) == 0)
UNP_PCB_UNLOCK(unp2);
}
connect_self:
UNP_PCB_UNLOCK(unp);
UNP_REF_LIST_LOCK();
while (!LIST_EMPTY(&unp->unp_refs)) {
@ -864,6 +872,10 @@ uipc_disconnect(struct socket *so)
UNP_PCB_UNLOCK(unp);
return (0);
}
if (unp == unp2) {
if (unp_pcb_rele(unp) == 0)
UNP_PCB_UNLOCK(unp);
}
unp_pcb_owned_lock2(unp, unp2, freed);
if (__predict_false(freed)) {
UNP_PCB_UNLOCK(unp);
@ -1925,7 +1937,9 @@ unp_drop(struct unpcb *unp)
if (so)
so->so_error = ECONNRESET;
unp2 = unp->unp_conn;
if (unp2 != NULL) {
if (unp2 == unp) {
unp_disconnect(unp, unp2);
} else if (unp2 != NULL) {
unp_pcb_hold(unp2);
unp_pcb_owned_lock2(unp, unp2, freed);
unp_disconnect(unp, unp2);