Re: [RFC] Null Namespaces

From: Andy Lutomirski

Date: Mon Jul 06 2026 - 13:15:28 EST


On Mon, Jul 6, 2026 at 8:31 AM Christian Brauner <brauner@xxxxxxxxxx> wrote:
>
> On Thu, Jul 02, 2026 at 11:34:01AM +0200, Christian Brauner wrote:
> > On Mon, Jun 29, 2026 at 02:06:55PM -0700, Andy Lutomirski wrote:
> > > On Mon, Jun 29, 2026 at 4:45 AM Christian Brauner <brauner@xxxxxxxxxx> wrote:
> > > >
> > >
> > > > But I guess the even simpler model would be to copy what I've been doing
> > > > for pidfs:
> > > >
> > > > +static struct path nullfs_root_path = {};
> > > > +
> > > > +void nullfs_get_root(struct path *path)
> > > > +{
> > > > + *path = nullfs_root_path;
> > > > + path_get(path);
> > > > +}
> > > > +
> > > > static void __init init_mount_tree(void)
> > > > {
> > > > struct vfsmount *mnt, *nullfs_mnt;
> > > > @@ -6209,6 +6217,8 @@ static void __init init_mount_tree(void)
> > > > /* Mount mutable rootfs on top of nullfs. */
> > > > root.mnt = nullfs_mnt;
> > > > root.dentry = nullfs_mnt->mnt_root;
> > > > + nullfs_root_path.mnt = nullfs_mnt;
> > > > + pidfs_root_path.dentry = nullfs_mnt->mnt_root;
> > > >
> > > > LOCK_MOUNT_EXACT(mp, &root);
> > > > if (unlikely(IS_ERR(mp.parent)))
> > > > diff --git a/include/uapi/linux/fcntl.h b/include/uapi/linux/fcntl.h
> > > > index aadfbf6e0cb3..f55c87c70b78 100644
> > > > --- a/include/uapi/linux/fcntl.h
> > > > +++ b/include/uapi/linux/fcntl.h
> > > > @@ -124,6 +124,7 @@ struct delegation {
> > > >
> > > > #define FD_PIDFS_ROOT -10002 /* Root of the pidfs filesystem */
> > > > #define FD_NSFS_ROOT -10003 /* Root of the nsfs filesystem */
> > > > +#define FD_NULLFS_ROOT -10004 /* Root of the nullfs filesystem */
> > > > #define FD_INVALID -10009 /* Invalid file descriptor: -10000 - EBADF = -10009 */
> > > >
> > > > /* Generic flags for the *at(2) family of syscalls. */
> > > >
> > > > we then add fchroot() (overdue anyway) and then teach both fchdir() and
> > > > fchroot() to honor FD_NULLFS_ROOT. Then a process may shed its fs state
> > > > and move itself into nullfs. Restrict *chdir() and *chroot() for said
> > > > process via seccomp and it's locked in forever as well.
> > > >
> > >
> > > One thing comes to mind that might need a bit of care: this would give
> > > an API for any task to get an fd to a directory that lives in the init
> > > mount namespace. It's not at all obvious to me that this is dangerous
> > > or even observable (you're not about to find a setuid program in
> > > nullfs), but I think it's at least worth a tiny bit of consideration.
> >
> > Yes, I thought about this as well. But it doesn't have to be this way.
> > Every mount namespaces has nullfs as it's root ever since I introduced
> > it. Which means FD_NULLFS_ROOT can also just mean "nullfs within that
> > specific mount namespace". That's fine.
> >
> > For my FD_FAILFS_ROOT proposal it would be enough if we make failfs
> > SB_KERNMOUNT which means it's logically distinct from every mount
> > namespace. I think that might be the right thing to do. I need to spend
> > one or more brain cycles on this though.
>
> I had to take a long drive on Sunday and I kept thinking about both
> FD_NULLFS_ROOT and FD_FAILFS_ROOT and ofc there are some things to
> consider/discuss.
>
> I think the straightforward solution to FD_NULLFS_ROOT would be to just:
>
> - make it always available
> - refer to the caller's mount namespace nullfs
> - work with fchroot()/fchdir()
>
> So I considered two chroot() use-cases for the sake of simplicity:
>
> (1) You want to isolate yourself for the sake of lookup
>
> (2) You want to isolate yourself to assemble a "private mount tree" but
> not really be in a separate namespace (very odd use-case... but it
> helps to make a point).
>
> The problem with this approach is that everyone who chroots into the
> nullfs root would suffer from the problem that any mount on top of it is
> still visible. So that kinda makes it pointless for both (1) and (2).

Ugh.

You're at least functionally correct, although this all reminds me
that I have never felt like Linux's vfs mount hierarchy makes any
sense:

root@debian:/mnt/empty# mkdir hidden
root@debian:/mnt/empty# mount --bind /usr .
root@debian:/mnt/empty# ls
hidden
root@debian:/mnt/empty# ls hidden/..
bin games include lib libexec local sbin share src

>
> FD_FAILFS_ROOT on the other hand should work fine. It would be a shared
> single fs with SB_KERNMOUNT and you can't do anything at all:
>
> - no lookup
> - no creation (duh)
> - no stat
> - no mounting
>
> We could certainly allow a chroot() into this which would mean from that
> point onward all your lookup bust be relative to a given file
> descriptor. Anything that requires absolute paths would fail. Which also
> means any absolute symlink would fail afaict. It's kinda like an
> fs_struct variant of: RESOLVE_BENEATH where a FD_FAILFS_ROOT fs_struct
> forces you to provide an actual dirfd...
>
> chroot()ing back into anything non-empty would necessarily require
> CAP_SYS_CHROOT. And since you're chroot()ed you can't unshare a userns.
> So the only way to get out of this is by having access to a file
> descriptor to a mount namespace that the caller has privilege over and
> can setns() into. So it's mostly a "throw-away-the-key" moment.
>
> > > But if this happens, maybe we could finally land one of the patches to
> > > enable unprivileged chroot? It's been tried a few times.
> > >
> > > https://lore.kernel.org/lkml/0e2f0f54e19bff53a3739ecfddb4ffa9a6dbde4d.1327858005.git.luto@xxxxxxxxxxxxxx/
> > >
> > > https://lore.kernel.org/all/20210316203633.424794-2-mic@xxxxxxxxxxx/
> > >
> > > I think the need for it has reduced a tiny bit with user namespaces,
> > > as you can sort of emulate it by unsharing your user namespace and
> > > thus getting enough privilege, but this is rather heavyweight and
> > > limiting.
> >
> > I think we could make that work with both FD_NULLFS_ROOT and
> > FD_FAILFS_ROOT...
> >
> > >
> > >
> > > If all of the above landed, then the old chroot /var/empty kludge that
> > > security-minded programs have done for decades could finally be
> > > modernized and not require any privilege :)
> >
> > I think I like it.
> >
> > > Hmm, thinking aloud: every now and then someone brings up the idea of
> > > having an fd (really an OFD) that points to a file or a directory but
> > > carries less in the way of permissions/capabilities than the usual
> > > OFDs. If we had a way to make an OFD to a directory that forced
> > > RESOLVE_BENEATH (or RESOLVE_IN_ROOT) and that propagated that
> > > restriction to anything you open using it, and if an unprivileged
> > > process could chroot itself to nullfs, then we would be getting quite
> > > close to what Capsicum can do.
> >
> > Next steps. I hear you volunteering...
>
> Thanks for the braindump. I need to find time to process it all.
>