From nobody Fri Jun 19 04:05:08 2026 X-Original-To: dev-commits-src-main@mlmmj.nyi.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2610:1c1:1:606c::19:1]) by mlmmj.nyi.freebsd.org (Postfix) with ESMTP id 4ghPBY2Hdhz6hPfq for ; Fri, 19 Jun 2026 04:05:09 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "YR1" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ghPBY1dwmz3XFL for ; Fri, 19 Jun 2026 04:05:09 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781841909; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=i5O9sXvPxGSqGp7JSPwSDjVykCjZ4ZVybZofh4gKaf4=; b=gxLoW8uleK/kbATAvwuR9mC47DOySEtyZmBHZ/TiMDtLByWf1c3KDNOaqHU0J6bIP00R3t v5/gfCGxQM9k0mKuaU75vCHwmE7JKcyZb6noO6iY/mm0TzGRXQgRhve6/EcpWqKAqytRRv vq9YO0UR2BPXqprGrbb6chhfJQF7w3LSZooD/WwK0Qi/HNuM1oQdghJLNrGsNjIA5TlF2h fwk77sRsfYd+r8BJicHh8ypkmz2wZNY1A/Jwt3Q5VEncWnZBeu8gd5ZGzHFv/CfCRk/h86 /hLzZqI/3DG2dz3kW1qt3/dKP+sN0T1oXKdXnI2kpaCNGs/rE1uwRIankZlz+w== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781841909; a=rsa-sha256; cv=none; b=s/6a8j6inxf789A1xjc66DeYh/5Ktt6mkzjiXbkBjS8U2MNeTrqBa9DagwH2ix3FhNbB61 Iz3VKriQ5LNxyzjdiO7aMcf5WCltb2+dxV2FW2hduQR+Sxyqz82L8ncbDHJ060IoU7d9mm 9h1lu28NN6BzkQZEA6JXwg95FV6rvs9iILQZxYg6W0GRZK/MvzkNYxaQM4gQzydmHzFNl4 lETiz6sbwQY5AxEOshaMrvnfQcZA25iyjiVj4rILGix7d1VAaYFcsb4xvUKNtCiABsN7Kj sCxDj0uMPluhdApeRNUBFYfmtpXG81vQSpq7INCh1J2pqCEp/O/9iiwv/546Mw== ARC-Authentication-Results: i=1; mx1.freebsd.org; none ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781841909; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=i5O9sXvPxGSqGp7JSPwSDjVykCjZ4ZVybZofh4gKaf4=; b=iZK9WhKkSEL/xd4GLYwQFp3k5M3ac4t184KxCpNvBOGLDZIVzGTBkDz5TmlqzKaq0KVw11 qZIEPfszx9uA3zmlcCj/QG8DzqudXhWhMh/THb/3tsAR121fUDn6u4pv9iFFr3LLBGUm7Q YDCkpB4i8V9fuMpQ1ZKTER1EWL4XXnJ4WgmTYQp4ZRh6T7RJ3+C+Gs4LhObX7LYPf8hG1t cmhN6Semt4hn2Gw6kPql8n9LxKaWfVcy9JfFCiDKcVU7oERRJ1/dF1quN/lIiyKtg6R4+x WCfNdulHqBgKK57BxI71qjbtYd5jDpkZOMnd/gVhIeV2tEmL72mb9y01kACoEQ== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4ghPBX6zNCzDBy for ; Fri, 19 Jun 2026 04:05:08 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 19cc8 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Fri, 19 Jun 2026 04:05:08 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-main@FreeBSD.org From: Kyle Evans Subject: git: 9b16399225f8 - main - unix: implement basic SO_PASSRIGHTS functionality List-Id: Commit messages for the main branch of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-main List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-main@freebsd.org Sender: owner-dev-commits-src-main@FreeBSD.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: kevans X-Git-Repository: src X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 9b16399225f8acfba675cf0a9312dd9eac563ce3 Auto-Submitted: auto-generated Date: Fri, 19 Jun 2026 04:05:08 +0000 Message-Id: <6a34bff4.19cc8.634a666@gitrepo.freebsd.org> The branch main has been updated by kevans: URL: https://cgit.FreeBSD.org/src/commit/?id=9b16399225f8acfba675cf0a9312dd9eac563ce3 commit 9b16399225f8acfba675cf0a9312dd9eac563ce3 Author: Kyle Evans AuthorDate: 2026-06-19 04:03:30 +0000 Commit: Kyle Evans CommitDate: 2026-06-19 04:03:30 +0000 unix: implement basic SO_PASSRIGHTS functionality With exception to sockopt functionality, implement the so_options flag in unix(4) itself. The general argument for the flag is that SCM_RIGHTS can be used maliciously for, e.g., a DoS that the receiving side can't avoid if it is expecting other control messages. This option gives the receiver a way to disable SCM_RIGHTS on the sender-side, surfacing an EPERM to them instead. This seems to match the semantics that Linux offers. If an SCM_RIGHTS was already sent before we disabled SO_PASSRIGHTS, then a subsequent recvmsg(2) will silently discard any in-flight files. This has the downside of punting a file with the potential to hang over to the deferred-close task, but perhaps usage of the option would discourage folks from attempting to take advantage of that possibility anyways. Various manpages updated to describe the new behavior. The ru_msgsnd accounting here might need to be re-evaluated: some error paths will increment it while others will not, and it isn't really clearly labelled. At some point in the operation enough work has been done that one could consider it enough resources to count, but it's not obvious that it should work this way. This change doesn't attempt to address that. Reviewed by: glebius Differential Revision: https://reviews.freebsd.org/D57423 --- lib/libsys/getsockopt.2 | 3 ++- lib/libsys/send.2 | 14 +++++++++-- share/man/man4/unix.4 | 13 ++++++++++- sys/kern/uipc_usrreq.c | 62 +++++++++++++++++++++++++++++++++++++------------ sys/sys/socket.h | 1 + 5 files changed, 74 insertions(+), 19 deletions(-) diff --git a/lib/libsys/getsockopt.2 b/lib/libsys/getsockopt.2 index 85d94e014631..1cfbe6ee913a 100644 --- a/lib/libsys/getsockopt.2 +++ b/lib/libsys/getsockopt.2 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 21, 2026 +.Dd June 3, 2026 .Dt GETSOCKOPT 2 .Os .Sh NAME @@ -192,6 +192,7 @@ The following options are recognized in .It Dv SO_NO_OFFLOAD Ta "disables protocol offloads" .It Dv SO_NO_DDP Ta "disables direct data placement offload" .It Dv SO_SPLICE Ta "splice two sockets together" +.It Dv SO_PASSRIGHTS Ta "enables passing of SCM_RIGHTS over unix(4) sockets" .El .Pp .Dv SO_DEBUG diff --git a/lib/libsys/send.2 b/lib/libsys/send.2 index 678eef139f4a..3a66e0f30471 100644 --- a/lib/libsys/send.2 +++ b/lib/libsys/send.2 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd April 27, 2020 +.Dd June 3, 2026 .Dt SEND 2 .Os .Sh NAME @@ -250,6 +250,15 @@ The process using a socket was jailed and the source address specified in the IP header did not match the IP address bound to the prison. +.It Bq Er EPERM +The +.Fa msg +contained an +.Dv SCM_RIGHTS +control message, and the receiving +.Xr unix 4 +socket is configured to reject new +.Dv SCM_RIGHTS . .It Bq Er EPIPE The socket is unable to send anymore data .Dv ( SBS_CANTSENDMORE @@ -265,7 +274,8 @@ is not connected. .Xr select 2 , .Xr socket 2 , .Xr write 2 , -.Xr CMSG_DATA 3 +.Xr CMSG_DATA 3 , +.Xr unix 4 .Sh HISTORY The .Fn send diff --git a/share/man/man4/unix.4 b/share/man/man4/unix.4 index 2fdfde225b14..f83f9ddffea3 100644 --- a/share/man/man4/unix.4 +++ b/share/man/man4/unix.4 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd October 31, 2024 +.Dd June 3, 2026 .Dt UNIX 4 .Os .Sh NAME @@ -163,9 +163,20 @@ depending on whether is passed in the .Xr recvmsg 2 call. +.Pp Descriptors that are awaiting delivery, or that are purposely not received, are automatically closed by the system when the destination socket is closed. +The receiving socket can reject +.Dv SCM_RIGHTS +at the +.Xr sendmsg 2 +call with the socket option +.Dv SO_PASSRIGHTS , +which can be set with +.Xr setsockopt 2 +and tested with +.Xr getsockopt 2 . .Pp Credentials of the sending process can be transmitted explicitly using a control message of type diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index b28aed291895..ad6d546afa25 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -303,9 +303,10 @@ static void unp_scan(struct mbuf *, void (*)(struct filedescent **, int)); static void unp_discard(struct file *); static void unp_freerights(struct filedescent **, int); static int unp_internalize(struct mbuf *, struct mchain *, - struct thread *); + struct thread *, int *); static void unp_internalize_fp(struct file *); -static int unp_externalize(struct mbuf *, struct mbuf **, int); +static int unp_externalize(const struct socket *, struct mbuf *, + struct mbuf **, int); static int unp_externalize_fp(struct file *); static void unp_addsockcred(struct thread *, struct mchain *, int); static void unp_process_defers(void * __unused, int); @@ -527,6 +528,7 @@ common: UNP_PCB_LOCK_INIT(unp); unp->unp_socket = so; so->so_pcb = unp; + so->so_options |= SO_PASSRIGHTS; refcount_init(&unp->unp_refcount, 1); unp->unp_mode = ACCESSPERMS; @@ -1112,7 +1114,7 @@ uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr, struct mchain mc, cmc; size_t resid, sent; bool nonblock, eor, aio; - int error; + int error, needsopts; MPASS((uio0 != NULL && m == NULL) || (m != NULL && uio0 == NULL)); MPASS(m == NULL || c == NULL); @@ -1128,9 +1130,11 @@ uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr, cmc = MCHAIN_INITIALIZER(&cmc); sent = 0; aio = false; + needsopts = 0; if (m == NULL) { - if (c != NULL && (error = unp_internalize(c, &cmc, td))) + if (c != NULL && + (error = unp_internalize(c, &cmc, td, &needsopts))) goto out; /* * This function may read more data from the uio than it would @@ -1176,6 +1180,14 @@ uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr, if (__predict_false((error = uipc_lock_peer(so, &unp2)) != 0)) goto out3; + /* Check for SO_PASS* flags */ + so2 = unp2->unp_socket; + if ((atomic_load_int(&so2->so_options) & needsopts) != needsopts) { + error = EPERM; + UNP_PCB_UNLOCK(unp2); + goto out3; + } + if (unp2->unp_flags & UNP_WANTCRED_MASK) { /* * Credentials are passed only once on SOCK_STREAM and @@ -1193,7 +1205,6 @@ uipc_sosend_stream_or_seqpacket(struct socket *so, struct sockaddr *addr, * observe the SBS_CANTRCVMORE and our sorele() will finalize peer's * socket destruction. */ - so2 = unp2->unp_socket; soref(so2); UNP_PCB_UNLOCK(unp2); sb = &so2->so_rcv; @@ -1560,7 +1571,7 @@ restart: * is fine that we need to perform pretty complex * operation here to reconstruct the buffer. */ - error = unp_externalize(control, controlp, flags); + error = unp_externalize(so, control, controlp, flags); control = m_free(control); if (__predict_false(error && control != NULL)) { struct mchain cmc; @@ -1959,11 +1970,11 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, struct mbuf *f; u_int cc, ctl, mbcnt; u_int dcc __diagused, dctl __diagused, dmbcnt __diagused; - int error; + int error, needsopts; MPASS((uio != NULL && m == NULL) || (m != NULL && uio == NULL)); - error = 0; + error = needsopts = 0; f = NULL; if (__predict_false(flags & MSG_OOB)) { @@ -1983,7 +1994,8 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, f = m_gethdr(M_WAITOK, MT_SONAME); cc = m->m_pkthdr.len; mbcnt = MSIZE + m->m_pkthdr.memlen; - if (c != NULL && (error = unp_internalize(c, &cmc, td))) + if (c != NULL && + (error = unp_internalize(c, &cmc, td, &needsopts))) goto out; } else { struct mchain mc; @@ -2049,6 +2061,13 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, } } + /* Check for SO_PASS* flags */ + so2 = unp2->unp_socket; + if ((atomic_load_int(&so2->so_options) & needsopts) != needsopts) { + error = EPERM; + goto out4; + } + if (unp2->unp_flags & UNP_WANTCRED_MASK) unp_addsockcred(td, &cmc, unp2->unp_flags); if (unp->unp_addr != NULL) @@ -2117,7 +2136,6 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, * would accumulate counters from all connected buffers potentially * having sb_ccc > sb_hiwat or sb_mbcnt > sb_mbmax. */ - so2 = unp2->unp_socket; sb = (addr == NULL) ? &so->so_snd : &so2->so_rcv; SOCK_RECVBUF_LOCK(so2); if (uipc_dgram_sbspace(sb, cc + ctl, mbcnt)) { @@ -2143,6 +2161,7 @@ uipc_sosend_dgram(struct socket *so, struct sockaddr *addr, struct uio *uio, } } +out4: if (addr != NULL) unp_disconnect(unp, unp2); else @@ -2345,7 +2364,7 @@ uipc_soreceive_dgram(struct socket *so, struct sockaddr **psa, struct uio *uio, * without MT_DATA mbufs. */ while (m != NULL && m->m_type == MT_CONTROL) { - error = unp_externalize(m, controlp, flags); + error = unp_externalize(so, m, controlp, flags); m = m_free(m); if (error != 0) { SOCK_IO_RECV_UNLOCK(so); @@ -3495,7 +3514,8 @@ restrict_rights(struct file *fp, struct thread *td) } static int -unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags) +unp_externalize(const struct socket *so, struct mbuf *control, + struct mbuf **controlp, int flags) { struct thread *td = curthread; /* XXX */ struct cmsghdr *cm = mtod(control, struct cmsghdr *); @@ -3527,8 +3547,18 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags) goto next; fdep = data; - /* If we're not outputting the descriptors free them. */ - if (error || controlp == NULL) { + /* + * If we're not outputting the descriptors, free them. + * + * In the case of having revoked SCM_PASSRIGHTS, the + * receiver must have toggled it before trying to + * receive control messages- we'll take that as a signal + * that they didn't want these, but they raced against + * the sender trying to pass files anyways. + */ + if (error || controlp == NULL || + (atomic_load_int(&so->so_options) & + SO_PASSRIGHTS) == 0) { unp_freerights(fdep, newfds); goto next; } @@ -3673,7 +3703,8 @@ unp_internalize_cleanup_rights(struct mbuf *control) } static int -unp_internalize(struct mbuf *control, struct mchain *mc, struct thread *td) +unp_internalize(struct mbuf *control, struct mchain *mc, struct thread *td, + int *needsopts) { struct proc *p; struct filedesc *fdesc; @@ -3729,6 +3760,7 @@ unp_internalize(struct mbuf *control, struct mchain *mc, struct thread *td) break; case SCM_RIGHTS: + *needsopts |= SO_PASSRIGHTS; oldfds = datalen / sizeof (int); if (oldfds == 0) continue; diff --git a/sys/sys/socket.h b/sys/sys/socket.h index 8477e122520a..d9c31d572b4f 100644 --- a/sys/sys/socket.h +++ b/sys/sys/socket.h @@ -147,6 +147,7 @@ typedef __uintptr_t uintptr_t; #define SO_NO_DDP 0x00008000 /* int; disable direct data placement */ #define SO_REUSEPORT_LB 0x00010000 /* int; reuse with load balancing */ #define SO_RERROR 0x00020000 /* int; keep track of receive errors */ +#define SO_PASSRIGHTS 0x00040000 /* int; unix(4) accepts SCM_RIGHTS */ /* * Additional options, not kept in so_options.