From nobody Fri Jun 19 04:05:08 2026 X-Original-To: dev-commits-src-all@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 4ghPBf5zszz6hPpH for ; Fri, 19 Jun 2026 04:05:14 +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 4ghPBf2Sjwz3XFb for ; Fri, 19 Jun 2026 04:05:14 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1781841914; 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=BvvxkW7b6ISEJEOmtB/JKNM/N69LZecgWuHNhu9LwlW0zY287jDuM/orNIpGcb0adU51xS vfX32ODUEvw1+r/Weh8wb9PK6hZ18q2z82dCcMIHoiycWwJ/5msZwpBBNDiSh0rvHL/rwU RLj9rgKGF+BadRca++x9ntPTydH8c+lcVXqMZLE2uS6DAKQqeaNxugVgQ0ODXE3aU2rnBh 4N9FP16/rGKUzOmnmsU0pCwLz+SKv4zEj0Jm0Axc5bT7SM8ILdnMPc7mH6uw4X+z9Iud/4 EHtaOy9NojXLcBKfqywUkr27cyHSsCscvMEPjNQhudCk2z/j2NodzDsQOi0qgA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1781841914; a=rsa-sha256; cv=none; b=OmjQwpsNsyzfiOqddKYR8fBIAb1ps8vJnKNzC/1VuoZ+e/k1p4RTVHKE2qxxnSzTCiHJrk cBiLMXddom2knlO/BQTFG/qp0p4DBZiLydjk0mpeut6w6NEE4Zsp3M5CwnuwpotIx++IRL 5WS/JoFucifZHQtEl1IrqvaRnwYkJmG6PBB/WZMpprraf//ne0hYe84RscYWETBog9fiEw OXVXcUO1JsBbbzjq0bmJ3E0/yjpZvO12JxArSwgoKkyMivvpWSNuU1taeOF9goZLBLu2aj A2Z9l0PFHFJ9Or9o39FbKUUYaTBDKscSGfoYb/hjVyEZpT6ayE6W5Oh3svehmA== 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=1781841914; 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=rJUHlDf+6A9Ido7M1Ygqerr3A10kCy0Ij6qIadaMIVnETBB7iUGpkZTLD1ET5JRY54m8+1 ODDmigaX3y4my12qDjpt3ptBfEyzijpkDEV4SftkhEv5wZ8YS6ac1495fcZuBVFB73Porl uYEne0kr/Ay2agctExDkHu5df3FbXa2kmeqWx8SF4OefxtFhgzE90/J6Vr8NfJ9zuRW3F5 gaQLvIYH08PyuxKkpDB/FvUdGBwvS8t3BPQtngY8WRrDLG4f0omam+GQAqJoyprcZEb7KA IR//EDxgUNzWe223u/qnyaLOxaBPvD3hkdBTGmMM4ZBFdvR3ZGXSCzxRA9aW5g== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4ghPBd71nhzCRJ for ; Fri, 19 Jun 2026 04:05:13 +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 all branches of the src repository List-Archive: https://lists.freebsd.org/archives/dev-commits-src-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-src-all@freebsd.org Sender: owner-dev-commits-src-all@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.