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;

Reply via email to