From nobody Tue Feb 24 16:26:37 2026 X-Original-To: dev-commits-doc-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 4fL35H162yz6SS6p for ; Tue, 24 Feb 2026 16:26:43 +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 "R12" (not verified)) by mx1.freebsd.org (Postfix) with ESMTPS id 4fL35H0mn5z3tPk for ; Tue, 24 Feb 2026 16:26:43 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1771950403; 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=9lJ4fPkEbwJDpJ9gmaj01SO4NUCuP/hzcuDHyZXRIbw=; b=g/E3zVYg0TdbZErWnJ8T7ItG8qI2Q8npLbdFEwJ8K7OWebdJDzvqblzfUEU6rzNMaYJXd+ mixvNN2E6NRSQsbjkeuvf+g1/pY1eeza5/UTn5j2vyLKg02NEP4gEywxc7FO60S0vWaDK0 FWfQ+Hky+zZdirAtKREYSW0WFg1YRsQ3KK4/I+7R+jEVJUwt2ugVnWEshwaglrWYGhqEtr nBdfqA1IhcAg/Flpvhb7f5m/dO01lHVTp6NnBpr+EnArhNYAo1f5nURVMH1LAFBfyzAmHH wBEzxa87SixjxG29TI7khcPSua8M3+Wo9KWqDHwotkC1AMEIlgpN8QH0cFPdmA== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1771950403; a=rsa-sha256; cv=none; b=E1AUTS9gmqPbtjoFQPRU+bMYvGkTVArhs4WVrYGzUqnlqByyPg1pMrmcJzpcGWA810IIC2 7xYgWAcNBsaaKo39OZDE1i3o9JXh1mDKaV+FQIN2FXdZAV7vz1VK3D4M9AkstqLggOYIF3 6vGLoIw8hdHaq0gqj2+84XaDNu6PeEr1L1EaNyxyaAp+N4Cw047iq1JMJKelK2JHhu2+ud hKVlKSCQOI2LJuRjy0j2Go0isHoCJstNVb9I8k6UNYGrPD9RWV4JXutHcnfKW3SBq5aCIl n/m1QLV1YhAzVNuG66Ln4pz46kFlBK+2t//qwtaxcnuQPB1mC/KQ5f0h0KJlHg== 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=1771950403; 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=9lJ4fPkEbwJDpJ9gmaj01SO4NUCuP/hzcuDHyZXRIbw=; b=TmpFQbZK4MuqzMV3AXlDp/gO3InJodNzwnPpkhA8kOUBVhEzHIVZKEhsfAsr8aKPvNgjuI 3UQNfnHmNh6P+jn0ArwZ+jk+SbI4KYhaTcJ+Iv+IekXmDpeAUW1S8/TLIa6tC0IxLsjOBi 9i/MDSODbU4cE6D119x48lqUVzbu8fuJWsbgjzC2XnkWR9Wgh7eNZKb7xYnuq7q8RdyBgi M4M7ao3tMXLTGZpOH3HdHB2JzOoSlVH45U2hq0keTejOGcDGlkRQkAWaQ6GhMvAMONBAWN I3qhwEFeNmZrn8HlJPiR2ZntS+0pmsZTTSQbFDwfzli6pnlHiDMM+GVgMaR00Q== Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4fL35H08S1z7J9 for ; Tue, 24 Feb 2026 16:26:43 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 3143f by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Tue, 24 Feb 2026 16:26:37 +0000 To: doc-committers@FreeBSD.org, dev-commits-doc-all@FreeBSD.org From: Gordon Tetlow Subject: git: 2cbb6f0c05 - main - Add SA-26:04 and SA-26:05. List-Id: Commit messages for all branches of the doc repository List-Archive: https://lists.freebsd.org/archives/dev-commits-doc-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-doc-all@freebsd.org Sender: owner-dev-commits-doc-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: gordon X-Git-Repository: doc X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 2cbb6f0c05d1872fca1d0ec949ff7b844ca1cc0d Auto-Submitted: auto-generated Date: Tue, 24 Feb 2026 16:26:37 +0000 Message-Id: <699dd13d.3143f.108afe9e@gitrepo.freebsd.org> The branch main has been updated by gordon: URL: https://cgit.FreeBSD.org/doc/commit/?id=2cbb6f0c05d1872fca1d0ec949ff7b844ca1cc0d commit 2cbb6f0c05d1872fca1d0ec949ff7b844ca1cc0d Author: Gordon Tetlow AuthorDate: 2026-02-24 16:26:10 +0000 Commit: Gordon Tetlow CommitDate: 2026-02-24 16:26:10 +0000 Add SA-26:04 and SA-26:05. Approved by: so --- website/data/security/advisories.toml | 8 + .../security/advisories/FreeBSD-SA-26:04.jail.asc | 165 +++ .../security/advisories/FreeBSD-SA-26:05.route.asc | 161 +++ .../static/security/patches/SA-26:04/jail-13.patch | 1132 +++++++++++++++++++ .../security/patches/SA-26:04/jail-13.patch.asc | 16 + .../static/security/patches/SA-26:04/jail-14.patch | 1173 ++++++++++++++++++++ .../security/patches/SA-26:04/jail-14.patch.asc | 16 + .../static/security/patches/SA-26:05/route.patch | 13 + .../security/patches/SA-26:05/route.patch.asc | 16 + 9 files changed, 2700 insertions(+) diff --git a/website/data/security/advisories.toml b/website/data/security/advisories.toml index c1c95b201a..18fcab9bf1 100644 --- a/website/data/security/advisories.toml +++ b/website/data/security/advisories.toml @@ -1,6 +1,14 @@ # Sort advisories by year, month and day # $FreeBSD$ +[[advisories]] +name = "FreeBSD-SA-26:05.route" +date = "2026-02-24" + +[[advisories]] +name = "FreeBSD-SA-26:04.jail" +date = "2026-02-24" + [[advisories]] name = "FreeBSD-SA-26:03.blocklistd" date = "2026-02-10" diff --git a/website/static/security/advisories/FreeBSD-SA-26:04.jail.asc b/website/static/security/advisories/FreeBSD-SA-26:04.jail.asc new file mode 100644 index 0000000000..92716c237c --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:04.jail.asc @@ -0,0 +1,165 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:04.jail Security Advisory + The FreeBSD Project + +Topic: Jail chroot escape via fd exchange with a different jail + +Category: core +Module: jail +Announced: 2026-02-24 +Affects: FreeBSD 14.3 and 13.5. +Corrected: 2025-07-29 12:49:03 UTC (stable/14, 14.3-STABLE) + 2026-02-24 16:01:32 UTC (releng/14.3, 14.3-RELEASE-p9) + 2026-02-09 20:44:00 UTC (stable/13, 13.4-STABLE) + 2026-02-24 16:04:42 UTC (releng/13.5, 13.5-RELEASE-p10) +CVE Name: CVE-2025-15576 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +Jails are an operating system virtualization technology which allow +administrators to confine processes within an environment with limited ability +to affect the system outside of that environment. In particular, jailed +processes typically have their filesystem access restricted by a chroot-like +mechanism. + +nullfs(4) is a pseudo-filesystem which allows a directory to be mounted at +another point in the filesystem hierarchy. + +unix domain sockets are a mechanism for interprocess communication. They +behave similarly to Internet sockets but are identified by names in the local +filesystem. unix domain sockets allow processes to exchange file descriptors +using control messages. + +II. Problem Description + +If two sibling jails are restricted to separate filesystem trees, which is to +say that neither of the two jail root directories is an ancestor of the other, +jailed processes may nonetheless be able to access a shared directory via a +nullfs mount, if the administrator has configured one. + +In this case, cooperating processes in the two jails may establish a connection +using a unix domain socket and exchange directory descriptors with each other. + +When performing a filesystem name lookup, at each step of the lookup, the +kernel checks whether the lookup would descend below the jail root of the +current process. If the jail root directory is not encountered, the lookup +continues. + +III. Impact + +In a configuration where processes in two different jails are able to exchange +file descriptors using a unix domain socket, it is possible for a jailed +process to receive a directory for a descriptor that is below that process' +jail root. This enables full filesystem access for a jailed process, breaking +the chroot. + +Note that the system administrator is still responsible for ensuring that an +unprivileged user on the jail host is not able to pass directory descriptors +to a jailed process, even in a patched kernel. + +IV. Workaround + +No workaround is available. Note that in order to exploit this problem, an +attacker requires control over processes in two jails which share a nullfs +mount in which a unix socket can be installed. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms, +or the i386 platform on FreeBSD 13, which were not installed using base +system packages, can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +[FreeBSD 14.3] +# fetch https://security.FreeBSD.org/patches/SA-26:04/jail-14.patch +# fetch https://security.FreeBSD.org/patches/SA-26:04/jail-14.patch.asc +# gpg --verify jail-14.patch.asc + +[FreeBSD 13.5] +# fetch https://security.FreeBSD.org/patches/SA-26:04/jail-13.patch +# fetch https://security.FreeBSD.org/patches/SA-26:04/jail-13.patch.asc +# gpg --verify jail-13.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/14/ 3ad3ab5f9b6e stable/14-n272076 +releng/14.3/ fbc35b3e6615 releng/14.3-n271471 +stable/13/ 73530e4c2ea9 stable/13-n259752 +releng/13.5/ e6b96891ef7c releng/13.5-n259202 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmmd0NwACgkQbljekB8A +Gu9WrxAAzgjxobnwhy+3RrD4XSOViKv7Dk6va/cqZtiP+SEv1lwM86P4aeUbqCOL +XPGItri1El9gQoBYsLS/b5ODbevV/CBaTeZbGwm129B9xdrJ4lQgQrDBh3qgo55k +OxQTnZbJgnF0YtjcSnkC+oWs4selpADEevEe2ohVUrV4OjXjVoCc3hVibPPwFh+8 +G5lPqcI26kXXimjb+zC+5yFQwNy/an9sYeiVnYceCuAOxxoV0Uf23Z5Ndc5oPBUD +lYMfrfuqmuhX6AtxTSU7x4BDx4MGTDIMYjU/LXptzMI5bpvqUy4F4lqx0t8vXV8F +T8vpbzGt8uhyRoD9Wp9LCIS7PpjBNm3YINY4Zd9z46tiC5ItTSV5mkJzatDB2zW+ +4iMcFQxHFGksHyrGn3epYKm1C3NtbKc5lEVHnKZqg11H2xUtDkTRn8AVcy8a9Bh+ +FDo1+yAb96W5by9UGA7nCdF8xwr9+ea/k6JDDfHxgVsOKzOgXsh7wmJ686kTIT2I +2REIMLY79xs50Lii5EMvN1oSjXxb7+WFphe+XCoH39JDTI3ekg7EpnFHcXLzMaVt +rciDlmPBU8h5A8U8GyI359DbIlha2IY5R2yC/opHUkOq/wBDJUZcL2y41BEH11jb +uFxRavagcRePVrSHSuXOH1vSdmsdrtl/h7HBP83J4X6ZG3nnr90= +=cwB8 +-----END PGP SIGNATURE----- diff --git a/website/static/security/advisories/FreeBSD-SA-26:05.route.asc b/website/static/security/advisories/FreeBSD-SA-26:05.route.asc new file mode 100644 index 0000000000..9e4edbab24 --- /dev/null +++ b/website/static/security/advisories/FreeBSD-SA-26:05.route.asc @@ -0,0 +1,161 @@ +-----BEGIN PGP SIGNED MESSAGE----- +Hash: SHA512 + +============================================================================= +FreeBSD-SA-26:05.route Security Advisory + The FreeBSD Project + +Topic: Local DoS and possible privilege escalation via routing sockets + +Category: core +Module: route +Announced: 2026-02-24 +Credits: Adam Crosser of the Praetorian Labs team +Affects: All supported versions of FreeBSD. +Corrected: 2026-02-24 16:00:26 UTC (stable/15, 15.0-STABLE) + 2026-02-24 16:00:39 UTC (releng/15.0, 15.0-RELEASE-p4) + 2026-02-24 16:00:56 UTC (stable/14, 14.4-STABLE) + 2026-02-24 16:02:31 UTC (releng/14.4, 14.4-RC1) + 2026-02-24 16:01:35 UTC (releng/14.3, 14.3-RELEASE-p9) + 2026-02-24 16:03:17 UTC (stable/13, 13.5-STABLE) + 2026-02-24 16:04:45 UTC (releng/13.5, 13.5-RELEASE-p10) +CVE Name: CVE-2026-3038 + +For general information regarding FreeBSD Security Advisories, +including descriptions of the fields above, security branches, and the +following sections, please visit . + +I. Background + +The routing socket interface, route(4), lets users query the state of the +kernel's routing tables. Most routing socket operations require root +privileges, but unprivileged users may send RTM_GET messages to obtain +information about routing table entries. + +II. Problem Description + +The rtsock_msg_buffer() function serializes routing information into a buffer. +As a part of this, it copies sockaddr structures into a sockaddr_storage +structure on the stack. It assumes that the source sockaddr length field had +already been validated, but this is not necessarily the case, and it's possible +for a malicious userspace program to craft a request which triggers a 127-byte +overflow. + +In practice, this overflow immediately overwrites the canary for the +rtsock_msg_buffer() stack frame, resulting in a panic once the function +returns. + +III. Impact + +The bug allows an unprivileged user to crash the kernel by triggering a stack +buffer overflow in rtsock_msg_buffer(). In particular, the overflow will +corrupt a stack canary value that is verified when the function returns; this +mitigates the impact of the stack overflow by triggering a kernel panic. + +Other kernel bugs may exist which allow userspace to find the canary value and +thus defeat the mitigation, at which point local privilege escalation may be +possible. + +IV. Workaround + +No workaround is available. + +V. Solution + +Upgrade your vulnerable system to a supported FreeBSD stable or +release / security branch (releng) dated after the correction date. + +Perform one of the following: + +1) To update your vulnerable system installed from base system packages: + +Systems running a 15.0-RELEASE version of FreeBSD on the amd64 or arm64 +platforms, which were installed using base system packages, can be updated +via the pkg(8) utility: + +# pkg upgrade -r FreeBSD-base +# shutdown -r +10min "Rebooting for a security update" + +2) To update your vulnerable system installed from binary distribution sets: + +Systems running a RELEASE version of FreeBSD on the amd64 or arm64 platforms, +or the i386 platform on FreeBSD 13, which were not installed using base +system packages, can be updated via the freebsd-update(8) utility: + +# freebsd-update fetch +# freebsd-update install +# shutdown -r +10min "Rebooting for a security update" + +3) To update your vulnerable system via a source code patch: + +The following patches have been verified to apply to the applicable +FreeBSD release branches. + +a) Download the relevant patch from the location below, and verify the +detached PGP signature using your PGP utility. + +# fetch https://security.FreeBSD.org/patches/SA-26:05/route.patch +# fetch https://security.FreeBSD.org/patches/SA-26:05/route.patch.asc +# gpg --verify route.patch.asc + +b) Apply the patch. Execute the following commands as root: + +# cd /usr/src +# patch < /path/to/patch + +c) Recompile your kernel as described in + and reboot the +system. + +VI. Correction details + +This issue is corrected as of the corresponding Git commit hash in the +following stable and release branches: + +Branch/path Hash Revision +- ------------------------------------------------------------------------- +stable/15/ df932377e7dd stable/15-n282455 +releng/15.0/ 5de6a55c70ba releng/15.0-n281009 +stable/14/ 1eb2beb3686c stable/14-n273785 +releng/14.4/ 7465d0b094b7 releng/14.4-n273667 +releng/14.3/ d521badafdaa releng/14.3-n271474 +stable/13/ 8b476ffc4ea3 stable/13-n259798 +releng/13.5/ c2e2bfbd9e09 releng/13.5-n259205 +- ------------------------------------------------------------------------- + +Run the following command to see which files were modified by a +particular commit: + +# git show --stat + +Or visit the following URL, replacing NNNNNN with the hash: + + + +To determine the commit count in a working tree (for comparison against +nNNNNNN in the table above), run: + +# git rev-list --count --first-parent HEAD + +VII. References + + + +The latest revision of this advisory is available at + +-----BEGIN PGP SIGNATURE----- + +iQIzBAEBCgAdFiEEthUnfoEIffdcgYM7bljekB8AGu8FAmmdz7cACgkQbljekB8A +Gu+9ehAAziBGPEv4RtXdh5OPqRkmJrZbxYNsiDmsqCO1alaEq/P64uLSI3ShOEf7 +K51oW4P+pukw13mJ7koDfWIFcJ5Jr4p+4vPIUenHafgXzOB9i6prn9kF0RFJN9zX +ziUaz8DGKd7B01eUoFj0p5l6rm00Z8q9l47ePOXfa+CS90lZxV/9z55UbmmCioQv +Ar98kPvaRmrmUqifuj72Jh1Wf69XLMDv4CI7BRumXIQnrHJ1xco4T9hHrHzPyNCf +cObfVsYMew/OGL2WgqfWvOEbmmC4mSW080kjPNmJxA+WG5fc0xQWaF41Kq1YDSWD +23SLqgjzTEP7zcsN/bW1k/7maf7lkKUWjtC/sjcqJRPfgWfHjDCVcMTKSjje65ld +Ml4sw4Ea2+jbOZqNcQhtFLo69atTu3oOgN2Gc677rvpkLl+HSivrX7D/1ULYfE0x +TbtW8Y8fqyNaPPOc1PktUcvQsZ1Sq8OKghOd/JAv1sKLZnxs61fWEMJKTJZEMHQB +NOnvw8PO2JPNMgJhPJz1CuD0pUCyTDqHYvfEI6TQikJmqKfrhAOBl8ccfNMyMmje +ZPW1f6hXud7c11OQXJ/u3QyBe7E+3v9MOf7Tn/mbFviwMx/xmG2VbgAuBBOVx6qb +QnHv9Ce+szmMV+9i0dj5KlsxhuFfUaDIIc9+iZ/1k8GkjkizDjE= +=V8QD +-----END PGP SIGNATURE----- diff --git a/website/static/security/patches/SA-26:04/jail-13.patch b/website/static/security/patches/SA-26:04/jail-13.patch new file mode 100644 index 0000000000..d59176e649 --- /dev/null +++ b/website/static/security/patches/SA-26:04/jail-13.patch @@ -0,0 +1,1132 @@ +--- sys/compat/cloudabi/cloudabi_fd.c.orig ++++ sys/compat/cloudabi/cloudabi_fd.c +@@ -389,7 +389,7 @@ + int error, oflags; + + /* Obtain file descriptor properties. */ +- error = fget_cap(td, uap->fd, cap_rights_init(&rights), &fp, ++ error = fget_cap(td, uap->fd, cap_rights_init(&rights), NULL, &fp, + &fcaps); + if (error != 0) + return (error); +--- sys/fs/fdescfs/fdesc_vnops.c.orig ++++ sys/fs/fdescfs/fdesc_vnops.c +@@ -515,7 +515,7 @@ + cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp); + } else { + error = getvnode_path(td, fd, +- cap_rights_init_one(&rights, CAP_EXTATTR_SET), &fp); ++ cap_rights_init_one(&rights, CAP_EXTATTR_SET), NULL, &fp); + } + if (error) { + /* +@@ -652,7 +652,7 @@ + VOP_UNLOCK(vn); + + td = curthread; +- error = fget_cap(td, fd_fd, &cap_no_rights, &fp, NULL); ++ error = fget_cap(td, fd_fd, &cap_no_rights, NULL, &fp, NULL); + if (error != 0) + goto out; + +--- sys/kern/kern_descrip.c.orig ++++ sys/kern/kern_descrip.c +@@ -114,7 +114,8 @@ + static void fdunused(struct filedesc *fdp, int fd); + static void fdused(struct filedesc *fdp, int fd); + static int fget_unlocked_seq(struct filedesc *fdp, int fd, +- cap_rights_t *needrightsp, struct file **fpp, seqc_t *seqp); ++ const cap_rights_t *needrightsp, uint8_t *flagsp, ++ struct file **fpp, seqc_t *seqp); + static int getmaxfd(struct thread *td); + static u_long *filecaps_copy_prep(const struct filecaps *src); + static void filecaps_copy_finish(const struct filecaps *src, +@@ -470,6 +471,8 @@ + return (error); + } + ++#define FD_RESOLVE_BENEATH 2 ++ + int + kern_fcntl(struct thread *td, int fd, int cmd, intptr_t arg) + { +@@ -519,7 +522,9 @@ + fde = fdeget_locked(fdp, fd); + if (fde != NULL) { + td->td_retval[0] = +- (fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0; ++ ((fde->fde_flags & UF_EXCLOSE) ? FD_CLOEXEC : 0) | ++ ((fde->fde_flags & UF_RESOLVE_BENEATH) ? ++ FD_RESOLVE_BENEATH : 0); + error = 0; + } + FILEDESC_SUNLOCK(fdp); +@@ -530,8 +535,13 @@ + FILEDESC_XLOCK(fdp); + fde = fdeget_locked(fdp, fd); + if (fde != NULL) { ++ /* ++ * UF_RESOLVE_BENEATH is sticky and cannot be cleared. ++ */ + fde->fde_flags = (fde->fde_flags & ~UF_EXCLOSE) | +- (arg & FD_CLOEXEC ? UF_EXCLOSE : 0); ++ ((arg & FD_CLOEXEC) != 0 ? UF_EXCLOSE : 0) | ++ ((arg & FD_RESOLVE_BENEATH) != 0 ? ++ UF_RESOLVE_BENEATH : 0); + error = 0; + } + FILEDESC_XUNLOCK(fdp); +@@ -2158,7 +2168,8 @@ + seqc_write_begin(&fde->fde_seqc); + #endif + fde->fde_file = fp; +- fde->fde_flags = (flags & O_CLOEXEC) != 0 ? UF_EXCLOSE : 0; ++ fde->fde_flags = ((flags & O_CLOEXEC) != 0 ? UF_EXCLOSE : 0) | ++ ((flags & O_RESOLVE_BENEATH) != 0 ? UF_RESOLVE_BENEATH : 0); + if (fcaps != NULL) + filecaps_move(fcaps, &fde->fde_caps); + else +@@ -2978,7 +2989,7 @@ + } + + int +-fget_cap_locked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, ++fget_cap_locked(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp, + struct file **fpp, struct filecaps *havecapsp) + { + struct filedescent *fde; +@@ -3010,8 +3021,8 @@ + } + + int +-fget_cap(struct thread *td, int fd, cap_rights_t *needrightsp, +- struct file **fpp, struct filecaps *havecapsp) ++fget_cap(struct thread *td, int fd, const cap_rights_t *needrightsp, ++ uint8_t *flagsp, struct file **fpp, struct filecaps *havecapsp) + { + struct filedesc *fdp = td->td_proc->p_fd; + int error; +@@ -3025,7 +3036,8 @@ + + *fpp = NULL; + for (;;) { +- error = fget_unlocked_seq(fdp, fd, needrightsp, &fp, &seq); ++ error = fget_unlocked_seq(fdp, fd, needrightsp, flagsp, &fp, ++ &seq); + if (error != 0) + return (error); + +@@ -3089,7 +3101,7 @@ + + #ifdef CAPABILITIES + int +-fgetvp_lookup_smr(int fd, struct nameidata *ndp, struct vnode **vpp, bool *fsearch) ++fgetvp_lookup_smr(int fd, struct nameidata *ndp, struct vnode **vpp, int *flagsp) + { + const struct filedescent *fde; + const struct fdescenttbl *fdt; +@@ -3099,6 +3111,7 @@ + const cap_rights_t *haverights; + cap_rights_t rights; + seqc_t seq; ++ int flags; + + VFS_SMR_ASSERT_ENTERED(); + +@@ -3117,7 +3130,9 @@ + return (EAGAIN); + if (__predict_false(cap_check_inline_transient(haverights, &rights))) + return (EAGAIN); +- *fsearch = ((fp->f_flag & FSEARCH) != 0); ++ flags = fp->f_flag & FSEARCH; ++ flags |= (fde->fde_flags & UF_RESOLVE_BENEATH) != 0 ? ++ O_RESOLVE_BENEATH : 0; + vp = fp->f_vnode; + if (__predict_false(vp == NULL)) { + return (EAGAIN); +@@ -3151,16 +3166,19 @@ + #endif + } + *vpp = vp; ++ *flagsp = flags; + return (0); + } + #else + int +-fgetvp_lookup_smr(int fd, struct nameidata *ndp, struct vnode **vpp, bool *fsearch) ++fgetvp_lookup_smr(int fd, struct nameidata *ndp, struct vnode **vpp, int *flagsp) + { ++ const struct filedescent *fde; + const struct fdescenttbl *fdt; + struct filedesc *fdp; + struct file *fp; + struct vnode *vp; ++ int flags; + + VFS_SMR_ASSERT_ENTERED(); + +@@ -3168,10 +3186,13 @@ + fdt = fdp->fd_files; + if (__predict_false((u_int)fd >= fdt->fdt_nfiles)) + return (EBADF); +- fp = fdt->fdt_ofiles[fd].fde_file; ++ fde = &fdt->fdt_ofiles[fd]; ++ fp = fde->fde_file; + if (__predict_false(fp == NULL)) + return (EAGAIN); +- *fsearch = ((fp->f_flag & FSEARCH) != 0); ++ flags = fp->f_flag & FSEARCH; ++ flags |= (fde->fde_flags & UF_RESOLVE_BENEATH) != 0 ? ++ O_RESOLVE_BENEATH : 0; + vp = fp->f_vnode; + if (__predict_false(vp == NULL || vp->v_type != VDIR)) { + return (EAGAIN); +@@ -3186,6 +3207,7 @@ + return (EAGAIN); + filecaps_fill(&ndp->ni_filecaps); + *vpp = vp; ++ *flagsp = flags; + return (0); + } + #endif +@@ -3199,13 +3221,15 @@ + struct componentname *cnp; + cap_rights_t rights; + int error; ++ uint8_t flags; + + td = curthread; + rights = *ndp->ni_rightsneeded; + cap_rights_set_one(&rights, CAP_LOOKUP); + cnp = &ndp->ni_cnd; + +- error = fget_cap(td, ndp->ni_dirfd, &rights, &fp, &ndp->ni_filecaps); ++ error = fget_cap(td, ndp->ni_dirfd, &rights, &flags, &fp, ++ &ndp->ni_filecaps); + if (__predict_false(error != 0)) + return (error); + if (__predict_false(fp->f_ops == &badfileops)) { +@@ -3223,6 +3247,10 @@ + */ + if ((fp->f_flag & FSEARCH) != 0) + cnp->cn_flags |= NOEXECCHECK; ++ if ((flags & UF_RESOLVE_BENEATH) != 0) { ++ cnp->cn_flags |= RBENEATH; ++ ndp->ni_resflags |= NIRES_BENEATH; ++ } + fdrop(fp, td); + + #ifdef CAPABILITIES +@@ -3256,12 +3284,10 @@ + } + + static int +-fget_unlocked_seq(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, +- struct file **fpp, seqc_t *seqp) ++fget_unlocked_seq(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp, ++ uint8_t *flagsp, struct file **fpp, seqc_t *seqp) + { +-#ifdef CAPABILITIES + const struct filedescent *fde; +-#endif + const struct fdescenttbl *fdt; + struct file *fp; + #ifdef CAPABILITIES +@@ -3269,6 +3295,7 @@ + cap_rights_t haverights; + int error; + #endif ++ uint8_t flags; + + fdt = fdp->fd_files; + if (__predict_false((u_int)fd >= fdt->fdt_nfiles)) +@@ -3287,10 +3314,13 @@ + fde = &fdt->fdt_ofiles[fd]; + haverights = *cap_rights_fde_inline(fde); + fp = fde->fde_file; ++ flags = fde->fde_flags; + if (!seqc_consistent(fd_seqc(fdt, fd), seq)) + continue; + #else +- fp = fdt->fdt_ofiles[fd].fde_file; ++ fde = &fdt->fdt_ofiles[fd]; ++ flags = fde->fde_flags; ++ fp = fde->fde_file; + #endif + if (fp == NULL) + return (EBADF); +@@ -3323,6 +3353,8 @@ + fdrop(fp, curthread); + } + *fpp = fp; ++ if (flagsp != NULL) ++ *flagsp = flags; + if (seqp != NULL) { + #ifdef CAPABILITIES + *seqp = seq; +@@ -3339,8 +3371,8 @@ + * racing with itself. + */ + int +-fget_unlocked(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, +- struct file **fpp) ++fget_unlocked_flags(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp, ++ uint8_t *flagsp, struct file **fpp) + { + #ifdef CAPABILITIES + const struct filedescent *fde; +@@ -3351,6 +3383,7 @@ + seqc_t seq; + const cap_rights_t *haverights; + #endif ++ uint8_t flags; + + fdt = fdp->fd_files; + if (__predict_false((u_int)fd >= fdt->fdt_nfiles)) { +@@ -3362,8 +3395,10 @@ + fde = &fdt->fdt_ofiles[fd]; + haverights = cap_rights_fde_inline(fde); + fp = fde->fde_file; ++ flags = fde->fde_flags; + #else + fp = fdt->fdt_ofiles[fd].fde_file; ++ flags = fdt->fdt_ofiles[fd].fde_flags; + #endif + if (__predict_false(fp == NULL)) + goto out_fallback; +@@ -3387,12 +3422,21 @@ + #endif + goto out_fdrop; + *fpp = fp; ++ if (flagsp != NULL) ++ *flagsp = flags; + return (0); + out_fdrop: + fdrop(fp, curthread); + out_fallback: + *fpp = NULL; +- return (fget_unlocked_seq(fdp, fd, needrightsp, fpp, NULL)); ++ return (fget_unlocked_seq(fdp, fd, needrightsp, flagsp, fpp, NULL)); ++} ++ ++int ++fget_unlocked(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp, ++ struct file **fpp) ++{ ++ return (fget_unlocked_flags(fdp, fd, needrightsp, NULL, fpp)); + } + + /* +@@ -3406,7 +3450,7 @@ + */ + #ifdef CAPABILITIES + int +-fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, ++fget_only_user(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp, + struct file **fpp) + { + const struct filedescent *fde; +@@ -3436,7 +3480,7 @@ + } + #else + int +-fget_only_user(struct filedesc *fdp, int fd, cap_rights_t *needrightsp, ++fget_only_user(struct filedesc *fdp, int fd, const cap_rights_t *needrightsp, + struct file **fpp) + { + struct file *fp; +@@ -3472,7 +3516,7 @@ + */ + static __inline int + _fget(struct thread *td, int fd, struct file **fpp, int flags, +- cap_rights_t *needrightsp) ++ const cap_rights_t *needrightsp) + { + struct filedesc *fdp; + struct file *fp; +@@ -3520,15 +3564,15 @@ + } + + int +-fget(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) ++fget(struct thread *td, int fd, const cap_rights_t *rightsp, struct file **fpp) + { + + return (_fget(td, fd, fpp, 0, rightsp)); + } + + int +-fget_mmap(struct thread *td, int fd, cap_rights_t *rightsp, vm_prot_t *maxprotp, +- struct file **fpp) ++fget_mmap(struct thread *td, int fd, const cap_rights_t *rightsp, ++ vm_prot_t *maxprotp, struct file **fpp) + { + int error; + #ifndef CAPABILITIES +@@ -3546,7 +3590,7 @@ + fdp = td->td_proc->p_fd; + MPASS(cap_rights_is_set(rightsp, CAP_MMAP)); + for (;;) { +- error = fget_unlocked_seq(fdp, fd, rightsp, &fp, &seq); ++ error = fget_unlocked_seq(fdp, fd, rightsp, NULL, &fp, &seq); + if (__predict_false(error != 0)) + return (error); + if (__predict_false(fp->f_ops == &badfileops)) { +@@ -3571,22 +3615,24 @@ + } + + int +-fget_read(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) ++fget_read(struct thread *td, int fd, const cap_rights_t *rightsp, ++ struct file **fpp) + { + + return (_fget(td, fd, fpp, FREAD, rightsp)); + } + + int +-fget_write(struct thread *td, int fd, cap_rights_t *rightsp, struct file **fpp) ++fget_write(struct thread *td, int fd, const cap_rights_t *rightsp, ++ struct file **fpp) + { + + return (_fget(td, fd, fpp, FWRITE, rightsp)); + } + + int +-fget_fcntl(struct thread *td, int fd, cap_rights_t *rightsp, int needfcntl, +- struct file **fpp) ++fget_fcntl(struct thread *td, int fd, const cap_rights_t *rightsp, ++ int needfcntl, struct file **fpp) + { + struct filedesc *fdp = td->td_proc->p_fd; + #ifndef CAPABILITIES +@@ -3599,7 +3645,7 @@ + *fpp = NULL; + MPASS(cap_rights_is_set(rightsp, CAP_FCNTL)); + for (;;) { +- error = fget_unlocked_seq(fdp, fd, rightsp, &fp, &seq); ++ error = fget_unlocked_seq(fdp, fd, rightsp, NULL, &fp, &seq); + if (error != 0) + return (error); + error = cap_fcntl_check(fdp, fd, needfcntl); +@@ -3624,7 +3670,7 @@ + * XXX: what about the unused flags ? + */ + static __inline int +-_fgetvp(struct thread *td, int fd, int flags, cap_rights_t *needrightsp, ++_fgetvp(struct thread *td, int fd, int flags, const cap_rights_t *needrightsp, + struct vnode **vpp) + { + struct file *fp; +@@ -3646,21 +3692,22 @@ + } + + int +-fgetvp(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) ++fgetvp(struct thread *td, int fd, const cap_rights_t *rightsp, ++ struct vnode **vpp) + { + + return (_fgetvp(td, fd, 0, rightsp, vpp)); + } + + int +-fgetvp_rights(struct thread *td, int fd, cap_rights_t *needrightsp, ++fgetvp_rights(struct thread *td, int fd, const cap_rights_t *needrightsp, + struct filecaps *havecaps, struct vnode **vpp) + { + struct filecaps caps; + struct file *fp; + int error; + +- error = fget_cap(td, fd, needrightsp, &fp, &caps); ++ error = fget_cap(td, fd, needrightsp, NULL, &fp, &caps); + if (error != 0) + return (error); + if (fp->f_ops == &badfileops) { +@@ -3685,14 +3732,16 @@ + } + + int +-fgetvp_read(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) ++fgetvp_read(struct thread *td, int fd, const cap_rights_t *rightsp, ++ struct vnode **vpp) + { + + return (_fgetvp(td, fd, FREAD, rightsp, vpp)); + } + + int +-fgetvp_exec(struct thread *td, int fd, cap_rights_t *rightsp, struct vnode **vpp) ++fgetvp_exec(struct thread *td, int fd, const cap_rights_t *rightsp, ++ struct vnode **vpp) + { + + return (_fgetvp(td, fd, FEXEC, rightsp, vpp)); +@@ -3700,7 +3749,7 @@ + + #ifdef notyet + int +-fgetvp_write(struct thread *td, int fd, cap_rights_t *rightsp, ++fgetvp_write(struct thread *td, int fd, const cap_rights_t *rightsp, + struct vnode **vpp) + { + +--- sys/kern/sys_procdesc.c.orig ++++ sys/kern/sys_procdesc.c +@@ -121,7 +121,7 @@ + * died. + */ + int +-procdesc_find(struct thread *td, int fd, cap_rights_t *rightsp, ++procdesc_find(struct thread *td, int fd, const cap_rights_t *rightsp, + struct proc **p) + { + struct procdesc *pd; +@@ -168,7 +168,8 @@ + * Retrieve the PID associated with a process descriptor. + */ + int +-kern_pdgetpid(struct thread *td, int fd, cap_rights_t *rightsp, pid_t *pidp) ++kern_pdgetpid(struct thread *td, int fd, const cap_rights_t *rightsp, ++ pid_t *pidp) + { + struct file *fp; + int error; +--- sys/kern/uipc_mqueue.c.orig ++++ sys/kern/uipc_mqueue.c +@@ -2160,13 +2160,14 @@ + return (error); + } + +-typedef int (*_fgetf)(struct thread *, int, cap_rights_t *, struct file **); ++typedef int (*_fgetf)(struct thread *, int, const cap_rights_t *, ++ struct file **); + + /* + * Get message queue by giving file slot + */ + static int +-_getmq(struct thread *td, int fd, cap_rights_t *rightsp, _fgetf func, ++_getmq(struct thread *td, int fd, const cap_rights_t *rightsp, _fgetf func, + struct file **fpp, struct mqfs_node **ppn, struct mqueue **pmq) + { + struct mqfs_node *pn; +--- sys/kern/uipc_sem.c.orig ++++ sys/kern/uipc_sem.c +@@ -123,8 +123,8 @@ + semid_t *semidp, mode_t mode, unsigned int value, + int flags, int compat32); + static void ksem_drop(struct ksem *ks); +-static int ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp, +- struct file **fpp); ++static int ksem_get(struct thread *td, semid_t id, ++ const cap_rights_t *rightsp, struct file **fpp); + static struct ksem *ksem_hold(struct ksem *ks); + static void ksem_insert(char *path, Fnv32_t fnv, struct ksem *ks); + static struct ksem *ksem_lookup(char *path, Fnv32_t fnv); +@@ -588,7 +588,7 @@ + } + + static int +-ksem_get(struct thread *td, semid_t id, cap_rights_t *rightsp, ++ksem_get(struct thread *td, semid_t id, const cap_rights_t *rightsp, + struct file **fpp) + { + struct ksem *ks; +--- sys/kern/uipc_syscalls.c.orig ++++ sys/kern/uipc_syscalls.c +@@ -91,13 +91,13 @@ + * A reference on the file entry is held upon returning. + */ + int +-getsock_cap(struct thread *td, int fd, cap_rights_t *rightsp, ++getsock_cap(struct thread *td, int fd, const cap_rights_t *rightsp, + struct file **fpp, u_int *fflagp, struct filecaps *havecapsp) + { + struct file *fp; + int error; + +- error = fget_cap(td, fd, rightsp, &fp, havecapsp); ++ error = fget_cap(td, fd, rightsp, NULL, &fp, havecapsp); + if (error != 0) + return (error); + if (fp->f_type != DTYPE_SOCKET) { +@@ -727,7 +727,7 @@ + struct uio auio; + struct iovec *iov; + struct socket *so; +- cap_rights_t *rights; ++ const cap_rights_t *rights; + #ifdef KTRACE + struct uio *ktruio = NULL; + #endif +--- sys/kern/uipc_usrreq.c.orig ++++ sys/kern/uipc_usrreq.c +@@ -57,7 +57,6 @@ + * need a proper out-of-band + */ + +-#include + #include "opt_ddb.h" + + #include +@@ -67,6 +66,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -1993,22 +1993,34 @@ + free(fdep[0], M_FILECAPS); + } + ++static bool ++restrict_rights(struct file *fp, struct thread *td) ++{ ++ struct prison *prison1, *prison2; ++ ++ prison1 = fp->f_cred->cr_prison; ++ prison2 = td->td_ucred->cr_prison; ++ return (prison1 != prison2 && prison1->pr_root != prison2->pr_root && ++ prison2 != &prison0); ++} ++ + static int + unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags) *** 1797 LINES SKIPPED ***