git: 2c01d10f67b3 - main - unix: factor unp_vnode_peer() out of unp_connectat()

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

URL: https://cgit.FreeBSD.org/src/commit/?id=2c01d10f67b343254fd4e747f822a8cdcc326f9d

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

    unix: factor unp_vnode_peer() out of unp_connectat()
    
    Move the "resolve a locked vnode to the referenced peer socket it names"
    block into a helper.  Pure code motion: the caller now calls
    `unp_vnode_peer()` and keeps the `vput()`/connect/`sorele()` sequence.
    
    No functional change intended.
    
    Note: This refactor isn't really necessary as `unp_vnode_peer()` will
    only be called once throughout this entire patch series. I am just
    including it out of my personal preferences for decomposing tasks into
    smaller functions --- we can skip this patch if the reviewers don't like
    this.
    
    Signed-off-by: John Ericson <John.Ericson@Obsidian.Systems>
    Assisted-by: Claude Code (Claude Opus 4.8 and Fable 5)
    
    Reviewed by:    markj
    MFC after:      2 weeks
    Differential Revision:  https://reviews.freebsd.org/D58461
---
 sys/kern/uipc_usrreq.c | 85 ++++++++++++++++++++++++++++++--------------------
 1 file changed, 51 insertions(+), 34 deletions(-)

diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index ac8de59b57b2..418c43a8ac85 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/sys/kern/uipc_usrreq.c
@@ -296,6 +296,8 @@ static int	unp_connectat(int, struct socket *, struct sockaddr *,
 		    struct thread *, struct socket **);
 static int	unp_connect_peer(struct socket *, struct unpcb *,
 		    struct sockaddr **, struct thread *, bool);
+static int	unp_vnode_peer(struct vnode *, struct thread *,
+		    struct socket **);
 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);
@@ -2930,10 +2932,8 @@ static int
 unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
     struct thread *td, struct socket **referenced_peerp)
 {
-	struct mtx *vplock;
 	struct socket *so2;
-	struct vnode *vp;
-	struct unpcb *unp, *unp2;
+	struct unpcb *unp;
 	struct nameidata nd;
 	char buf[SOCK_MAXADDRLEN];
 	struct sockaddr *sa;
@@ -3001,40 +3001,15 @@ unp_connectat(int fd, struct socket *so, struct sockaddr *nam,
 	if (error)
 		goto out;
 	NDFREE_PNBUF(&nd);
-	vp = nd.ni_vp;
-	ASSERT_VOP_LOCKED(vp, "unp_connect");
 
 	/*
-	 * Resolve the vnode to a referenced peer socket, then drop the vnode
-	 * before connecting.  Holding a reference on the peer keeps it stable
-	 * in place of the per-vnode unp_vp_mtxpool lock, so no vnode lock is
-	 * held across unp_connect_peer() -- which is what the return_locked
-	 * datagram fast path needs, since vput() must not sleep while the peer
-	 * is locked.
+	 * Resolve the vnode to a referenced peer socket and drop the vnode
+	 * before connecting: the reference keeps the peer stable, so no vnode
+	 * lock is held across unp_connect_peer() (which matters for the
+	 * return_locked datagram fast path).
 	 */
-	if (vp->v_type != VSOCK) {
-		error = ENOTSOCK;
-		goto drop_vp;
-	}
-#ifdef MAC
-	error = mac_vnode_check_open(td->td_ucred, vp, VWRITE | VREAD);
-	if (error)
-		goto drop_vp;
-#endif
-	error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td);
-	if (error)
-		goto drop_vp;
-
-	vplock = mtx_pool_find(unp_vp_mtxpool, vp);
-	mtx_lock(vplock);
-	VOP_UNP_CONNECT(vp, &unp2);
-	if (unp2 == NULL)
-		error = ECONNREFUSED;
-	else
-		soref(so2 = unp2->unp_socket);
-	mtx_unlock(vplock);
-drop_vp:
-	vput(vp);
+	error = unp_vnode_peer(nd.ni_vp, td, &so2);
+	vput(nd.ni_vp);
 	if (error != 0)
 		goto out;
 	error = unp_connect_peer(so, sotounpcb(so2), &sa, td,
@@ -3056,6 +3031,48 @@ out:
 	return (error);
 }
 
+/*
+ * Resolve locked vnode 'vp' to the unix-domain socket it names and return a
+ * referenced peer socket in '*so2p'.  As the connect(2)-time resolution, this
+ * enforces the caller's authorization to reach the socket -- filesystem
+ * permission (VOP_ACCESS) and MAC (mac_vnode_check_open) -- which bare readers
+ * of the vnode->pcb binding, such as vfs_unp_reclaim(), deliberately skip.
+ *
+ * The returned reference keeps the peer stable for unp_connect_peer() once vp's
+ * per-vnode binding lock is dropped, so the caller must release it with
+ * sorele().  Does not consume 'vp'.
+ */
+static int
+unp_vnode_peer(struct vnode *vp, struct thread *td, struct socket **so2p)
+{
+	struct mtx *vplock;
+	struct unpcb *unp2;
+	int error;
+
+	ASSERT_VOP_LOCKED(vp, __func__);
+
+	if (vp->v_type != VSOCK)
+		return (ENOTSOCK);
+#ifdef MAC
+	error = mac_vnode_check_open(td->td_ucred, vp, VWRITE | VREAD);
+	if (error != 0)
+		return (error);
+#endif
+	error = VOP_ACCESS(vp, VWRITE, td->td_ucred, td);
+	if (error != 0)
+		return (error);
+
+	vplock = mtx_pool_find(unp_vp_mtxpool, vp);
+	mtx_lock(vplock);
+	VOP_UNP_CONNECT(vp, &unp2);
+	if (unp2 == NULL)
+		error = ECONNREFUSED;
+	else
+		soref(*so2p = unp2->unp_socket);
+	mtx_unlock(vplock);
+	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