Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 09 Feb 2026 20:44:00 +0000
From:      Mark Johnston <markj@FreeBSD.org>
To:        src-committers@FreeBSD.org, dev-commits-src-all@FreeBSD.org, dev-commits-src-branches@FreeBSD.org
Subject:   git: 73530e4c2ea9 - stable/13 - unix: Set O_RESOLVE_BENEATH on fds transferred between jails
Message-ID:  <698a4710.3fcb6.2c0bcaa1@gitrepo.freebsd.org>

index | next in thread | raw e-mail

The branch stable/13 has been updated by markj:

URL: https://cgit.FreeBSD.org/src/commit/?id=73530e4c2ea92564e393e0497f13dfac251a41b7

commit 73530e4c2ea92564e393e0497f13dfac251a41b7
Author:     Mark Johnston <markj@FreeBSD.org>
AuthorDate: 2025-06-24 20:05:37 +0000
Commit:     Mark Johnston <markj@FreeBSD.org>
CommitDate: 2026-02-09 17:48:55 +0000

    unix: Set O_RESOLVE_BENEATH on fds transferred between jails
    
    If a pair of jails with different filesystem roots is able to exchange
    SCM_RIGHTS messages (e.g., using a unix socket in a shared nullfs
    mount), a process in one jail can open a directory outside of the root
    of the second jail and then pass the fd to that second jail, allowing
    the receiving process to escape the jail chroot.
    
    Address this using the new FD_RESOLVE_BENEATH flag.  When externalizing
    an SCM_RIGHTS message into the receiving process, automatically set this
    flag on all new fds where a jail boundary is crossed.  This ensures that
    the receiver cannot do more than access files underneath the directory;
    in particular, the received fd cannot be used to access vnodes not
    accessible by the sender.
    
    PR:             262179
    Reviewed by:    kib
    MFC after:      3 weeks
    Differential Revision:  https://reviews.freebsd.org/D50371
    
    (cherry picked from commit 350ba9672a7f4f16e30534a603df577dfd083b3f)
---
 sys/amd64/conf/SYZKALLER |  5 +++++
 sys/kern/uipc_usrreq.c   | 31 +++++++++++++++++++++++--------
 2 files changed, 28 insertions(+), 8 deletions(-)

diff --git a/sys/amd64/conf/SYZKALLER b/sys/amd64/conf/SYZKALLER
new file mode 100644
index 000000000000..965841313616
--- /dev/null
+++ b/sys/amd64/conf/SYZKALLER
@@ -0,0 +1,5 @@
+include GENERIC-KASAN
+ident SYZKALLER
+
+options 	COVERAGE
+options 	KCOV
diff --git a/sys/kern/uipc_usrreq.c b/sys/kern/uipc_usrreq.c
index 0f5048a96e89..4043e7260d0f 100644
--- a/sys/kern/uipc_usrreq.c
+++ b/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 @@ unp_freerights(struct filedescent **fdep, int fdcount)
 	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)
 {
 	struct thread *td = curthread;		/* XXX */
 	struct cmsghdr *cm = mtod(control, struct cmsghdr *);
-	int i;
 	int *fdp;
 	struct filedesc *fdesc = td->td_proc->p_fd;
 	struct filedescent **fdep;
 	void *data;
 	socklen_t clen = control->m_len, datalen;
-	int error, newfds;
+	int error, fdflags, newfds;
 	u_int newlen;
 
 	UNP_LINK_UNLOCK_ASSERT();
 
+	fdflags = (flags & MSG_CMSG_CLOEXEC) ? O_CLOEXEC : 0;
+
 	error = 0;
 	if (controlp != NULL) /* controlp == NULL => free control messages */
 		*controlp = NULL;
@@ -2059,11 +2071,14 @@ unp_externalize(struct mbuf *control, struct mbuf **controlp, int flags)
 				*controlp = NULL;
 				goto next;
 			}
-			for (i = 0; i < newfds; i++, fdp++) {
-				_finstall(fdesc, fdep[i]->fde_file, *fdp,
-				    (flags & MSG_CMSG_CLOEXEC) != 0 ? O_CLOEXEC : 0,
-				    &fdep[i]->fde_caps);
-				unp_externalize_fp(fdep[i]->fde_file);
+			for (int i = 0; i < newfds; i++, fdp++) {
+				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);
+				unp_externalize_fp(fp);
 			}
 
 			/*


home | help

Want to link to this message? Use this
URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?698a4710.3fcb6.2c0bcaa1>