git: 2cbb6f0c05 - main - Add SA-26:04 and SA-26:05.
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Tue, 24 Feb 2026 16:26:37 UTC
The branch main has been updated by gordon:
URL: https://cgit.FreeBSD.org/doc/commit/?id=2cbb6f0c05d1872fca1d0ec949ff7b844ca1cc0d
commit 2cbb6f0c05d1872fca1d0ec949ff7b844ca1cc0d
Author: Gordon Tetlow <gordon@FreeBSD.org>
AuthorDate: 2026-02-24 16:26:10 +0000
Commit: Gordon Tetlow <gordon@FreeBSD.org>
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 <URL:https://security.FreeBSD.org/>.
+
+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
+<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> 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 <commit hash>
+
+Or visit the following URL, replacing NNNNNN with the hash:
+
+<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
+
+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
+
+<URL:https://www.cve.org/CVERecord?id=CVE-2025-15576>
+
+The latest revision of this advisory is available at
+<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:04.jail.asc>
+-----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 <URL:https://security.FreeBSD.org/>.
+
+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
+<URL:https://www.FreeBSD.org/handbook/kernelconfig.html> 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 <commit hash>
+
+Or visit the following URL, replacing NNNNNN with the hash:
+
+<URL:https://cgit.freebsd.org/src/commit/?id=NNNNNN>
+
+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
+
+<URL:https://www.cve.org/CVERecord?id=CVE-2026-3038>
+
+The latest revision of this advisory is available at
+<URL:https://security.FreeBSD.org/advisories/FreeBSD-SA-26:05.route.asc>
+-----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 <sys/cdefs.h>
+ #include "opt_ddb.h"
+
+ #include <sys/param.h>
+@@ -67,6 +66,7 @@
+ #include <sys/fcntl.h>
+ #include <sys/file.h>
+ #include <sys/filedesc.h>
++#include <sys/jail.h>
+ #include <sys/kernel.h>
+ #include <sys/lock.h>
+ #include <sys/malloc.h>
+@@ -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 ***