It looks like sometimes the `unp_conn' doesn't reloaded in
the "unp->unp_conn != unp2" check, and READ_ONCE() should prevent this.
unp_solock_peer(struct socket *so)
{
struct unpcb *unp, *unp2;
struct socket *so2;
unp = so->so_pcb;
again:
if ((unp2 = unp->unp_conn) == NULL)
return NULL;
so2 = unp2->unp_socket;
if (so < so2)
solock(so2);
else if (so > so2){
unp_ref(unp2);
sounlock(so);
solock(so2);
solock(so);
/* Datagram socket could be reconnected due to re-lock. */
if (unp->unp_conn != unp2) {
sounlock(so2);
unp_rele(unp2);
goto again;
}
unp_rele(unp2);
}
return so2;
}
Index: sys/kern/uipc_usrreq.c
===================================================================
RCS file: /cvs/src/sys/kern/uipc_usrreq.c,v
retrieving revision 1.167
diff -u -p -r1.167 uipc_usrreq.c
--- sys/kern/uipc_usrreq.c 2 Jul 2022 11:49:23 -0000 1.167
+++ sys/kern/uipc_usrreq.c 14 Jul 2022 01:08:22 -0000
@@ -154,7 +154,7 @@ unp_solock_peer(struct socket *so)
unp = so->so_pcb;
again:
- if ((unp2 = unp->unp_conn) == NULL)
+ if ((unp2 = READ_ONCE(unp->unp_conn)) == NULL)
return NULL;
so2 = unp2->unp_socket;
@@ -168,7 +168,7 @@ again:
solock(so);
/* Datagram socket could be reconnected due to re-lock. */
- if (unp->unp_conn != unp2) {
+ if (READ_ONCE(unp->unp_conn) != unp2) {
sounlock(so2);
unp_rele(unp2);
goto again;