git: 7977edb76560 - main - unix: split unp_connectat() in two

From: Mark Johnston <markj_at_FreeBSD.org>
Date: Mon, 10 Aug 2026 17:40:31 UTC
The branch main has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=7977edb7656082d90a539b85bd2a750b383602da

commit 7977edb7656082d90a539b85bd2a750b383602da
Author:     John Ericson <John.Ericson@Obsidian.Systems>
AuthorDate: 2026-08-10 15:04:18 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-08-10 17:31:21 +0000

    unix: split unp_connectat() in two
    
    Factor the second half — connecting to an already-resolved peer PCB —
    out into a new `unp_connect_peer()`, leaving `unp_connectat()` with the
    connection state machine and pathname resolution.  No functional change.
    
    The helper's contract: the caller guarantees stability of the peer PCB
    (vnode lock plus `unp_vp_mtxpool` lock for peers found via
    `VOP_UNP_CONNECT()`), has set `UNP_CONNECTING` on the connecting socket,
    and clears it again on error; the helper clears it on success.
    
    This prepares for connecting to a peer named by something other than a
    pathname.
    
    Signed-off-by: John Ericson <John.Ericson@Obsidian.Systems>
    
    Reviewed by:    markj
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D58404
---
 sys/kern/uipc_usrreq.c | 102 +++++++++++++++++++++++++++++--------------------
 1 file changed, 61 insertions(+), 41 deletions(-)

diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 0e3f0d13c12d..c1d00e66f614 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -294,6 +294,8 @@ static int	unp_connect(struct socket *, struct sockaddr *,
 		    struct thread *);
 static int	unp_connectat(int, struct socket *, struct sockaddr *,
 		    struct thread *, bool);
+static int	unp_connect_peer(struct socket *, struct unpcb *,
+		    struct sockaddr **, struct thread *, bool);
 static void	unp_connect2(struct socket *, struct socket *, bool);
 static void	unp_disconnect(struct unpcb *unp, struct unpcb *unp2);
 static void	unp_dispose(struct socket *so);
@@ -2889,8 +2891,7 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
 	struct mtx *vplock;
 	struct sockaddr_un *soun;
 	struct vnode *vp;
-	struct socket *so2;
-	struct unpcb *unp, *unp2, *unp3;
+	struct unpcb *unp, *unp2;
 	struct nameidata nd;
 	char buf[SOCK_MAXADDRLEN];
 	struct sockaddr *sa;
@@ -2979,40 +2980,80 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
 	if (error)
 		goto bad;
 
-	unp = sotounpcb(so);
-	KASSERT(unp != NULL, ("unp_connect: unp == NULL"));
-
 	vplock = mtx_pool_find(unp_vp_mtxpool, vp);
 	mtx_lock(vplock);
 	VOP_UNP_CONNECT(vp, &unp2);
-	if (unp2 == NULL) {
+	if (unp2 == NULL)
 		error = ECONNREFUSED;
-		goto bad2;
+	else
+		error = unp_connect_peer(so, unp2, &sa, td, return_locked);
+	mtx_unlock(vplock);
+bad:
+	if (vp != NULL) {
+		/*
+		 * If we are returning locked (called via uipc_sosend_dgram()),
+		 * we need to be sure that vput() won't sleep.  This is
+		 * guaranteed by VOP_UNP_CONNECT() call above and unp2 lock.
+		 * SOCK_STREAM/SEQPACKET can't request return_locked (yet).
+		 */
+		MPASS(!(return_locked && connreq));
+		vput(vp);
 	}
-	so2 = unp2->unp_socket;
-	if (so->so_type != so2->so_type) {
-		error = EPROTOTYPE;
-		goto bad2;
+	free(sa, M_SONAME);
+	if (__predict_false(error)) {
+		UNP_PCB_LOCK(unp);
+		KASSERT((unp->unp_flags & UNP_CONNECTING) != 0,
+		    ("%s: unp %p has UNP_CONNECTING clear", __func__, unp));
+		unp->unp_flags &= ~UNP_CONNECTING;
+		UNP_PCB_UNLOCK(unp);
 	}
+	return (error);
+}
+
+/*
+ * Second half of connecting a unix socket: 'so' is our connecting socket,
+ * with UNP_CONNECTING set, and 'unp2' is the PCB of the peer named by the
+ * caller, which must guarantee its stability (by holding a reference on the
+ * peer socket, or the vnode lock plus unp_vp_mtxpool lock for a peer found
+ * via VOP_UNP_CONNECT()).
+ *
+ * For connection-oriented sockets '*sap' points to a buffer to hold the
+ * listener's address; it is consumed (set to NULL) if used.  On success
+ * UNP_CONNECTING is cleared; on error the caller must clear it.
+ */
+static int
+unp_connect_peer(struct socket *so, struct unpcb *unp2, struct sockaddr **sap,
+    struct thread *td, bool return_locked)
+{
+	struct socket *so2;
+	struct unpcb *unp, *unp3;
+	int error;
+	bool connreq;
+
+	unp = sotounpcb(so);
+	KASSERT(unp != NULL, ("%s: unp == NULL", __func__));
+	connreq = (so->so_proto->pr_flags & PR_CONNREQUIRED) != 0;
+
+	so2 = unp2->unp_socket;
+	if (so->so_type != so2->so_type)
+		return (EPROTOTYPE);
 	if (connreq) {
 		if (SOLISTENING(so2))
 			so2 = solisten_clone(so2);
 		else
 			so2 = NULL;
-		if (so2 == NULL) {
-			error = ECONNREFUSED;
-			goto bad2;
-		}
+		if (so2 == NULL)
+			return (ECONNREFUSED);
 		if ((error = uipc_attach(so2, 0, NULL)) != 0) {
 			sodealloc(so2);
-			goto bad2;
+			return (error);
 		}
 		unp3 = sotounpcb(so2);
 		unp_pcb_lock_pair(unp2, unp3);
 		if (unp2->unp_addr != NULL) {
-			bcopy(unp2->unp_addr, sa, unp2->unp_addr->sun_len);
-			unp3->unp_addr = (struct sockaddr_un *) sa;
-			sa = NULL;
+			bcopy(unp2->unp_addr, *sap, unp2->unp_addr->sun_len);
+			unp3->unp_addr = (struct sockaddr_un *)*sap;
+			*sap = NULL;
 		}
 
 		unp_copy_peercred(td, unp3, unp, unp2);
@@ -3043,28 +3084,7 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
 	unp->unp_flags &= ~UNP_CONNECTING;
 	if (!return_locked)
 		unp_pcb_unlock_pair(unp, unp2);
-bad2:
-	mtx_unlock(vplock);
-bad:
-	if (vp != NULL) {
-		/*
-		 * If we are returning locked (called via uipc_sosend_dgram()),
-		 * we need to be sure that vput() won't sleep.  This is
-		 * guaranteed by VOP_UNP_CONNECT() call above and unp2 lock.
-		 * SOCK_STREAM/SEQPACKET can't request return_locked (yet).
-		 */
-		MPASS(!(return_locked && connreq));
-		vput(vp);
-	}
-	free(sa, M_SONAME);
-	if (__predict_false(error)) {
-		UNP_PCB_LOCK(unp);
-		KASSERT((unp->unp_flags & UNP_CONNECTING) != 0,
-		    ("%s: unp %p has UNP_CONNECTING clear", __func__, unp));
-		unp->unp_flags &= ~UNP_CONNECTING;
-		UNP_PCB_UNLOCK(unp);
-	}
-	return (error);
+	return (0);
 }
 
 /*