From nobody Tue Sep 29 16:00:09 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 4hvNDV58v4z6tlbr for ; Tue, 29 Sep 2026 16:00:10 +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 "YR2" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4hvNDT6b5Zz4gh3 for ; Tue, 29 Sep 2026 16:00:09 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790697610; 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=kREJ72s5iOct5Rii0Lj3kxhT+23nmqp3Vvd7G9t8G7Q=; b=VvST+81s2rGCnNqpSbV9ZaRW/o/NO2/PA8vN5jKvih7NYPietm+CmOXEoSzrXnx9gDDLi7 DNgNDXz9WYxiVSMLCUNcmlPAanXlxh3xOdGIAuNVkrh69rEVdGkeXOUZwblI2crNM4OET4 M/bhPKtDGIijSp4ZJYAa/Jcj3Ec3Pi2aVUjjZE0rizq8DBf+0ecn4YTRsrUGW2smqGEB5l TE7Lj5CxmrhJTHsWE4Osdlq8CLJ2cgockJBq/WS5y4rVFU+IPWb/dgDA4SowDSy8ecpwlH kwMBHg+ukBNJvZeAgAe3tMkJwURu/OnQdyhx0+WLNfkBSGa3U4lQP5Faqo+hmQ== ARC-Seal: i=1; a=rsa-sha256; d=freebsd.org; s=dkim; cv=none; t=1790697610; b=vR5NXsTJgd5Aec7gVEdIIB4jnBtlShmxFk/ZffgWS4ikwf4QxROAlsnuwRbglKXMvOuZbF xe/q/t1WKjFktlsMJNeIQgwS5rgIEPEgmqmDC84ovX4xIiSaxlY/qUo4JfyrIwBFiiOiDr DZqV1J/ZNF1qXfuwB/dOAGL6hybFXhG1XQVQnWss2EDi+Sv1BtxeJeEtDmcmXkjJrShyoJ oQEkmo8QG4pTJ5O2rYFMtxQF6qj96vCZQGke63zpSBgVY9Na9SVO1OJMmmZpgKBDQIbqek jFbpNJmnaQLUzmIGpxiYmybNsmohGe6sshUvkXoVxHn4Hz5f3HVb0r5qs/VImQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1790697610; 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=kREJ72s5iOct5Rii0Lj3kxhT+23nmqp3Vvd7G9t8G7Q=; b=b/6vVa6A8ZAwvVjmlUlrqh1f6iIK2u17Qi8c6ErywMR4fGcbG0TvMnyvQNM6dKoSCo+leX QwVAXExK+xp6x6UoGmHu4V9afhBGxCT1SRiQ9mtp1h7e/PApmiIUmZpo0k1CbXq8nn7bKG MIoLimKxr4CtIHcKVn3Yoipi/fWPlt+YqZ21NCIpSl2/RDhL88UBeSNyjicUZImMfKfEDh xuiEuAQ5vJQSOIlS84xUW2eHEFAAJ2PY63H+M9hY7K47mit9qItg8mxD68NjcsNGwF33Bs 25yN372n9uYpIzZx+MQPfWS0azOsfZtWFUSQCdl7HjZaef9WoKyxrtjhX55opA== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4hvNDT3NPfz1KVc for ; Tue, 29 Sep 2026 16:00:09 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 26868 by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 29 Sep 2026 16:00:09 +0000 To: src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org From: Mark Johnston Subject: git: 90d0b507cdf9 - releng/15.1 - unix: Preserve FD_RESOLVE_BENEATH when passing an fd 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: markj X-Git-Repository: src X-Git-Refname: refs/heads/releng/15.1 X-Git-Reftype: branch X-Git-Commit: 90d0b507cdf946df8cc9c7d14a67b9b2204fa9cd Auto-Submitted: auto-generated Date: Tue, 29 Sep 2026 16:00:09 +0000 Message-Id: <6abbe089.26868.161e16be@gitrepo.freebsd.org> The branch releng/15.1 has been updated by markj: URL: https://cgit.FreeBSD.org/src/commit/?id=90d0b507cdf946df8cc9c7d14a67b9b2204fa9cd commit 90d0b507cdf946df8cc9c7d14a67b9b2204fa9cd Author: Mark Johnston AuthorDate: 2026-07-24 20:06:05 +0000 Commit: Mark Johnston CommitDate: 2026-09-29 01:01:48 +0000 unix: Preserve FD_RESOLVE_BENEATH when passing an fd The FD_RESOLVE_BENEATH flag is supposed to be sticky. It's set when you receive an fd from a different jail and preserved by openat() etc.. However, if you send the fd to yourself, the flag is stripped since SCM_RIGHTS message don't preserve file descriptor flags. Fix this by preserving those flags and checking for UF_RESOLVE_BENEATH in restrict_rights(). Approved by: so Security: FreeBSD-SA-26:66.jail Security: CVE-2026-101306 Fixes: 350ba9672a7f ("unix: Set O_RESOLVE_BENEATH on fds transferred between jails") Reviewed by: kib MFC after: 1 week Sponsored by: The FreeBSD Foundation Differential Revision: https://reviews.freebsd.org/D58317 (cherry picked from commit 586e2b3d89d6e70ab7e4a88497b5f36d78719423) (cherry picked from commit 30758138f2e36d89bc30cb686796ccc4dfa8b475) --- sys/kern/uipc_usrreq.c | 27 +++++++++++++++++++-------- tests/sys/kern/unix_passfd_test.c | 31 +++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c index 6b3ef005b597..ccc02c98e3ac 100644 --- a/sys/kern/uipc_usrreq.c +++ b/sys/kern/uipc_usrreq.c @@ -3514,15 +3514,25 @@ unp_freerights(struct filedescent **fdep, int fdcount) free(fdep[0], M_FILECAPS); } -static bool -restrict_rights(struct file *fp, struct thread *td) +/* + * Flags to set on the receiving side when externalizing a file descriptor. + * When transferring fds between jails, ensure that the receiver cannot use + * a dirfd to escape the jail chroot. + */ +static int +externalize_fdflags(struct filedescent *fde, struct thread *td) { struct prison *prison1, *prison2; - prison1 = fp->f_cred->cr_prison; + if ((fde->fde_flags & UF_RESOLVE_BENEATH) != 0) + return (O_RESOLVE_BENEATH); + prison1 = fde->fde_file->f_cred->cr_prison; prison2 = td->td_ucred->cr_prison; - return (prison1 != prison2 && prison1->pr_root != prison2->pr_root && - prison2 != &prison0); + if (prison1 != prison2 && prison1->pr_root != prison2->pr_root && + prison2 != &prison0) + return (O_RESOLVE_BENEATH); + else + return (0); } static int @@ -3588,9 +3598,9 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags) struct file *fp; fp = fdep[i]->fde_file; - _finstall(fdesc, fp, *fdp, fdflags | - (restrict_rights(fp, td) ? - O_RESOLVE_BENEATH : 0), &fdep[i]->fde_caps); + _finstall(fdesc, fp, *fdp, + fdflags | externalize_fdflags(fdep[i], td), + &fdep[i]->fde_caps); unp_externalize_fp(fp); } @@ -3826,6 +3836,7 @@ unp_internalize(struct mbuf *control, struct mchain *mc, struct thread *td) fdep[i]->fde_file = fde->fde_file; filecaps_copy(&fde->fde_caps, &fdep[i]->fde_caps, true); + fdep[i]->fde_flags = fde->fde_flags; unp_internalize_fp(fdep[i]->fde_file); } FILEDESC_SUNLOCK(fdesc); diff --git a/tests/sys/kern/unix_passfd_test.c b/tests/sys/kern/unix_passfd_test.c index 0635bddb786e..c303c7337680 100644 --- a/tests/sys/kern/unix_passfd_test.c +++ b/tests/sys/kern/unix_passfd_test.c @@ -1308,6 +1308,36 @@ ATF_TC_CLEANUP(cross_jail_dirfd, tc) err(1, "jail_remove"); } +/* + * Verify that FD_RESOLVE_BENEATH is preserved when an fd is passed over a UNIX + * domain socket. + */ +ATF_TC_WITHOUT_HEAD(resolve_beneath_preserved); +ATF_TC_BODY(resolve_beneath_preserved, tc) +{ + int fd[2], getfd, putfd, fdflags; + + domainsocketpair(fd); + tempfile(&putfd); + + fdflags = fcntl(putfd, F_GETFD); + ATF_REQUIRE(fdflags != -1); + ATF_REQUIRE(fcntl(putfd, F_SETFD, fdflags | FD_RESOLVE_BENEATH) != -1); + ATF_REQUIRE((fcntl(putfd, F_GETFD) & FD_RESOLVE_BENEATH) != 0); + + sendfd(fd[0], putfd); + recvfd(fd[1], &getfd, 0); + + fdflags = fcntl(getfd, F_GETFD); + ATF_REQUIRE(fdflags != -1); + ATF_REQUIRE_MSG((fdflags & FD_RESOLVE_BENEATH) != 0, + "FD_RESOLVE_BENEATH was not preserved across SCM_RIGHTS transfer"); + + ATF_REQUIRE(close(putfd) == 0); + ATF_REQUIRE(close(getfd) == 0); + closesocketpair(fd); +} + ATF_TC_WITHOUT_HEAD(listening_socket); ATF_TC_BODY(listening_socket, tc) { @@ -1359,6 +1389,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, empty_rights_message); ATF_TP_ADD_TC(tp, control_creates_records); ATF_TP_ADD_TC(tp, cross_jail_dirfd); + ATF_TP_ADD_TC(tp, resolve_beneath_preserved); ATF_TP_ADD_TC(tp, listening_socket); return (atf_no_error());