* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
[not found] ` <20260723-work-failfs-v1-4-3f69b9a9e958@kernel.org>
@ 2026-07-23 12:49 ` Andy Lutomirski
2026-07-23 13:01 ` Christian Brauner
2026-07-23 14:42 ` Jann Horn
2026-07-23 14:07 ` Jann Horn
1 sibling, 2 replies; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-23 12:49 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, Andy Lutomirski, Jann Horn, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
On Thu, Jul 23, 2026 at 4:37 AM Christian Brauner <brauner@kernel.org> wrote:
>
> Once entered, failfs is a throw-away-the-key moment. The task is
> considered chrooted so it cannot create user namespaces to regain
> CAP_SYS_CHROOT. chroot()/fchroot() back out require CAP_SYS_CHROOT.
I kind of alluded to this earlier, but I don't think we should promise
this. In particular, I still think we should eventually allow a
non-chrooted process with no_new_privs to chroot to any valid
directory, and I think we should consider changing the definition of
current_chrooted() such that failfs-as-root would cause it to return
false. I'm also not sure why it's useful -- if I want to throw away
the key to accessing the normal contents of my mountns without
changing my mountns, I need to chroot somewhere (like failfs) *and* I
need to somehow prevent myself from getting an fd or a magic link back
to the ordinary mountns contents. If I somehow get such an fd,
preventing my from fchrooting to it doesn't seem to accomplish
anything, since I could traverse the fd with openat, etc just as
easily, or even fchdir to it and use relative paths.
My understanding of the exact workings of the mount tree is possibly
not good enough to specify the semantics I think are correct quite
exactly, but I think a decent approximation is that a task should be
considered to be not chrooted if its root is at the top of its mount
tree or is not in a mount tree at all.
--Andy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 0/7] fs: add failfs
[not found] <20260723-work-failfs-v1-0-3f69b9a9e958@kernel.org>
[not found] ` <20260723-work-failfs-v1-4-3f69b9a9e958@kernel.org>
@ 2026-07-23 12:53 ` Andy Lutomirski
[not found] ` <20260723-work-failfs-v1-7-3f69b9a9e958@kernel.org>
2 siblings, 0 replies; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-23 12:53 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, Andy Lutomirski, Jann Horn, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
On Thu, Jul 23, 2026 at 4:39 AM Christian Brauner <brauner@kernel.org> wrote:
>
[snipped excellent list of things one cannot do with failfs]
> A plain readlink() of
> such a link still works and shows "/".
This seems awkward to me. I feel like ls -ld /proc/PID/{cwd,exe}
should probably show something distinctive instead of "/". Maybe
readlink() should show "[failfs] /" or similar? Both admins or
developers trying to see what's going on and CRIU might want this.
IMO it's too bad that we don't have a nicely parseable readlink output
for oddities like links to deleted files, but oh well.
--Andy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 12:49 ` [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot() Andy Lutomirski
@ 2026-07-23 13:01 ` Christian Brauner
2026-07-23 13:50 ` Andy Lutomirski
2026-07-23 14:42 ` Jann Horn
1 sibling, 1 reply; 15+ messages in thread
From: Christian Brauner @ 2026-07-23 13:01 UTC (permalink / raw)
To: Andy Lutomirski
Cc: linux-fsdevel, Jann Horn, John Ericson, linux-api, H. Peter Anvin,
Kees Cook, Farid Zakaria, Alexander Viro, Jan Kara, linux-kernel,
Jonathan Corbet, linux-doc
On Thu, Jul 23, 2026 at 05:49:06AM -0700, Andy Lutomirski wrote:
> On Thu, Jul 23, 2026 at 4:37 AM Christian Brauner <brauner@kernel.org> wrote:
> >
> > Once entered, failfs is a throw-away-the-key moment. The task is
> > considered chrooted so it cannot create user namespaces to regain
> > CAP_SYS_CHROOT. chroot()/fchroot() back out require CAP_SYS_CHROOT.
>
> I kind of alluded to this earlier, but I don't think we should promise
> this. In particular, I still think we should eventually allow a
> non-chrooted process with no_new_privs to chroot to any valid
> directory, and I think we should consider changing the definition of
> current_chrooted() such that failfs-as-root would cause it to return
> false. I'm also not sure why it's useful -- if I want to throw away
I had considered that but introducing this change alongside this series
felt too spicy for me.
> the key to accessing the normal contents of my mountns without
> changing my mountns, I need to chroot somewhere (like failfs) *and* I
> need to somehow prevent myself from getting an fd or a magic link back
> to the ordinary mountns contents. If I somehow get such an fd,
> preventing my from fchrooting to it doesn't seem to accomplish
> anything, since I could traverse the fd with openat, etc just as
> easily, or even fchdir to it and use relative paths.
I think I generally agree with you. My main concern here is mostly about
changing very long-standing behavior here and making failfs special here
feels like it could easily misused.
>
> My understanding of the exact workings of the mount tree is possibly
> not good enough to specify the semantics I think are correct quite
> exactly, but I think a decent approximation is that a task should be
> considered to be not chrooted if its root is at the top of its mount
> tree or is not in a mount tree at all.
If we could altering this definition from the patchset I would be in
favor of it. I'm curious what your take is though.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 13:01 ` Christian Brauner
@ 2026-07-23 13:50 ` Andy Lutomirski
2026-07-23 14:35 ` Christian Brauner
0 siblings, 1 reply; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-23 13:50 UTC (permalink / raw)
To: Christian Brauner
Cc: Andy Lutomirski, linux-fsdevel, Jann Horn, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
On Thu, Jul 23, 2026 at 6:09 AM Christian Brauner <brauner@kernel.org> wrote:
>
> On Thu, Jul 23, 2026 at 05:49:06AM -0700, Andy Lutomirski wrote:
> > On Thu, Jul 23, 2026 at 4:37 AM Christian Brauner <brauner@kernel.org> wrote:
> > >
> > > Once entered, failfs is a throw-away-the-key moment. The task is
> > > considered chrooted so it cannot create user namespaces to regain
> > > CAP_SYS_CHROOT. chroot()/fchroot() back out require CAP_SYS_CHROOT.
> >
> > I kind of alluded to this earlier, but I don't think we should promise
> > this. In particular, I still think we should eventually allow a
> > non-chrooted process with no_new_privs to chroot to any valid
> > directory, and I think we should consider changing the definition of
> > current_chrooted() such that failfs-as-root would cause it to return
> > false. I'm also not sure why it's useful -- if I want to throw away
>
> I had considered that but introducing this change alongside this series
> felt too spicy for me.
I think I agree. My only actual objection to your series as is is
that your wording seems to make a promise that I think shouldn't be
made.
>
> > the key to accessing the normal contents of my mountns without
> > changing my mountns, I need to chroot somewhere (like failfs) *and* I
> > need to somehow prevent myself from getting an fd or a magic link back
> > to the ordinary mountns contents. If I somehow get such an fd,
> > preventing my from fchrooting to it doesn't seem to accomplish
> > anything, since I could traverse the fd with openat, etc just as
> > easily, or even fchdir to it and use relative paths.
>
> I think I generally agree with you. My main concern here is mostly about
> changing very long-standing behavior here and making failfs special here
> feels like it could easily misused.
I don't think either of us are really suggesting making failfs
special. I agree about the long-standing behavior.
>
> >
> > My understanding of the exact workings of the mount tree is possibly
> > not good enough to specify the semantics I think are correct quite
> > exactly, but I think a decent approximation is that a task should be
> > considered to be not chrooted if its root is at the top of its mount
> > tree or is not in a mount tree at all.
>
> If we could altering this definition from the patchset I would be in
> favor of it. I'm curious what your take is though.
>
I can't quite parse your question.
I have three sort-of-concrete proposals:
(a) current_chrooted returns false if follow_dotdot starting at
current root but with nd->root equal to the namespace root would
succeed and return the current root. I think this is fairly solid.
The main caveat I can think of is that someone could create a detached
mount tree, chroot to its root, then fork and have the child:
- drop privileges
- set no_new_privs
- chroot somewhere else
then the parent or another privileged task mounts that tree somewhere,
and the child could now access the parent of the mountpoint. Any
actual exploit based on this seems farfetched.
(b) separate the concepts of fs-root-for-absolute-paths and
fs-root-for-blocking-dotdot. IMO this actually makes sense but would
would be a much more intrusive change. We would have
fs->root_of_abspaths and fs->dotdot_blocking_root, and fchroot would
only change root_of_abspaths unless a flag is set asking to change
both. (And document that setting that flag is not advised and that
one should create a detached mount tree instead for 90% of use cases.)
And no_new_privs tasks would be allowed to freely change
root_of_abspaths.
I like (b) conceptually in the sense that I wish no one had ever
invented chroot-for-security in the first place. I'm not sure I like
it as an actual practical idea because it has a much larger scope.
Also (a) is almost equivalent as long as no one ever sets the root to
something that isn't the root of a mount tree, and it really would
confuse some people if "/.." was not equivalent to "./".
(c) Add a bit to fs_struct called something like chroot_locked.
chroot() sets the bit if it chroots anywhere other than "/" or its
equivalent. fchroot does not set it unless a flag asking for it is
set. Switching mount namespace clears the bit. If you have
no_new_privs and don't have chroot_locked then you can chroot.
--Andy
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 7/7] Documentation: add failfs documentation
[not found] ` <20260723-work-failfs-v1-7-3f69b9a9e958@kernel.org>
@ 2026-07-23 14:04 ` Miquel Sabaté Solà
0 siblings, 0 replies; 15+ messages in thread
From: Miquel Sabaté Solà @ 2026-07-23 14:04 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, Andy Lutomirski, Jann Horn, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
[-- Attachment #1: Type: text/plain, Size: 4065 bytes --]
Christian Brauner @ 2026-07-23 13:30 +02:
> Document the failfs semantics, the FD_FAILFS_ROOT sentinel, the
> fchroot() entry requirements, and the ways back out.
>
> Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
> ---
> Documentation/filesystems/failfs.rst | 64 ++++++++++++++++++++++++++++++++++++
> Documentation/filesystems/index.rst | 1 +
> 2 files changed, 65 insertions(+)
>
> diff --git a/Documentation/filesystems/failfs.rst b/Documentation/filesystems/failfs.rst
> new file mode 100644
> index 000000000000..46d91525916b
> --- /dev/null
> +++ b/Documentation/filesystems/failfs.rst
> @@ -0,0 +1,64 @@
> +.. SPDX-License-Identifier: GPL-2.0
> +
> +======
> +failfs
> +======
> +
> +failfs is a kernel-internal filesystem that fails every operation
> +reaching it with ``EOPNOTSUPP``. It is the counterpart to nullfs. Where
> +nullfs is a permanently empty failfs means "nothing is supported here". It
> +cannot be mounted from userspace, nothing can be mounted on top of it. It
> +cannot be cloned.
A small change here makes the wording more clear: from 'Where nullfs is
a permanently empty failfs means "nothing is supported here"' to 'Where
nullfs is permanently empty, failfs means "nothing is supported here"'.
> +
> +The only way into it is the ``FD_FAILFS_ROOT`` file descriptor sentinel which
> +is understood by ``fchdir(2)`` and ``fchroot(2)``.
> +
> +Semantics
> +=========
> +
> +Every path walk of a component through failfs fails with
> +``EOPNOTSUPP`` before that component is parsed, including ``.``.
> +
> +The root itself cannot be opened at all not even with ``O_PATH``.
> +
> +A process with its working directory in failfs fails every
> +``AT_FDCWD``-relative lookup. As with any working directory that is
> +unreachable from the process root, the ``getcwd(2)`` system call returns
> +a path prefixed with ``(unreachable)``.
> +
> +A process with its root directory in failfs fails every absolute path
> +lookup including absolute symlinks and the interpreter of dynamically
> +linked binaries. In other words, this fails exec.
> +
> +Lookups anchored at explicit directory file descriptors keep working. It
> +is the ``fs_struct`` equivalent of ``RESOLVE_BENEATH``. The process must
> +anchor every lookup at a file descriptor it explicitly holds.
> +
> +Entering
> +========
> +
> +``fchroot(FD_FAILFS_ROOT, 0)`` requires ``CAP_SYS_CHROOT`` in the
> +caller's user namespace, mirroring ``chroot(2)``. Unprivileged callers
> +may enter if both of the following hold:
> +
> +* ``no_new_privs`` is set: setuid binaries on regular mounts remain
> + reachable via inherited directory file descriptors and executing them
> + with an unusable root directory is the classic confused deputy.
> +
> +* The caller is not already chrooted: the root directory is what
> + confines ``..`` resolution and the failfs root can never be reached by
> + walking up a real mount tree, so moving the root of a chrooted task to
> + failfs would allow it to escape its chroot via ``openat(fd, "..")``.
> +
> +Leaving
> +=======
> +
> +A process that entered failfs counts as chrooted. It cannot create user
> +namespaces to regain ``CAP_SYS_CHROOT``, and ``chroot(2)`` or
> +``fchroot(2)`` back out require ``CAP_SYS_CHROOT``. The only other exit
> +is ``setns(2)`` with a mount namespace file descriptor, which requires
> +``CAP_SYS_ADMIN`` over the target mount namespace as well as
> +``CAP_SYS_CHROOT`` and ``CAP_SYS_ADMIN`` in the caller's user namespace
> +and resets both root and working directory. A process that holds no such
> +file descriptor and restricts ``*chdir()``/``*chroot()``/``setns()`` via
> +seccomp has thrown away the key.
> diff --git a/Documentation/filesystems/index.rst b/Documentation/filesystems/index.rst
> index 1f71cf159547..734a45e51667 100644
> --- a/Documentation/filesystems/index.rst
> +++ b/Documentation/filesystems/index.rst
> @@ -91,6 +91,7 @@ Documentation for filesystem implementations.
> ext3
> ext4/index
> f2fs
> + failfs
> gfs2/index
> hfs
> hfsplus
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 897 bytes --]
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
[not found] ` <20260723-work-failfs-v1-4-3f69b9a9e958@kernel.org>
2026-07-23 12:49 ` [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot() Andy Lutomirski
@ 2026-07-23 14:07 ` Jann Horn
2026-07-23 16:56 ` Andy Lutomirski
1 sibling, 1 reply; 15+ messages in thread
From: Jann Horn @ 2026-07-23 14:07 UTC (permalink / raw)
To: Christian Brauner
Cc: linux-fsdevel, Andy Lutomirski, John Ericson, linux-api,
H. Peter Anvin, Kees Cook, Farid Zakaria, Alexander Viro,
Jan Kara, linux-kernel, Jonathan Corbet, linux-doc
On Thu, Jul 23, 2026 at 1:30 PM Christian Brauner <brauner@kernel.org> wrote:
> There's also some thought needed around shared fs_struct state. It's
> obviously possible to chroot into failfs with a shared fs_struct if the
> caller has CAP_SYS_CHROOT and shares the fs_struct or if the caller is
> no_new_privs and shares the fs_struct. The non-chrooted-currently
> requirement still applies.
Having CAP_SYS_CHROOT and sharing the fs_struct should not be an issue
because we already ensure that any tasks that share fs_struct are in
the same user namespace, so CAP_SYS_CHROOT is implicitly also held
relative to other users of the fs_struct. (In particular,
userns_install() and unshare_fs() ensure this.) (Except if LSMs get
involved with weird policy, I guess.) That's what makes the existing
chroot() syscall safe.
But yeah, the no_new_privs check I suggested is kind of a pain...
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 13:50 ` Andy Lutomirski
@ 2026-07-23 14:35 ` Christian Brauner
2026-07-23 16:59 ` Andy Lutomirski
0 siblings, 1 reply; 15+ messages in thread
From: Christian Brauner @ 2026-07-23 14:35 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Christian Brauner, linux-fsdevel, Jann Horn, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
On 2026-07-23 06:50 -0700, Andy Lutomirski wrote:
> On Thu, Jul 23, 2026 at 6:09 AM Christian Brauner <brauner@kernel.org> wrote:
> >
> > On Thu, Jul 23, 2026 at 05:49:06AM -0700, Andy Lutomirski wrote:
> > > On Thu, Jul 23, 2026 at 4:37 AM Christian Brauner <brauner@kernel.org> wrote:
> > > >
> > > > Once entered, failfs is a throw-away-the-key moment. The task is
> > > > considered chrooted so it cannot create user namespaces to regain
> > > > CAP_SYS_CHROOT. chroot()/fchroot() back out require CAP_SYS_CHROOT.
> > >
> > > I kind of alluded to this earlier, but I don't think we should promise
> > > this. In particular, I still think we should eventually allow a
> > > non-chrooted process with no_new_privs to chroot to any valid
> > > directory, and I think we should consider changing the definition of
> > > current_chrooted() such that failfs-as-root would cause it to return
> > > false. I'm also not sure why it's useful -- if I want to throw away
> >
> > I had considered that but introducing this change alongside this series
> > felt too spicy for me.
>
> I think I agree. My only actual objection to your series as is is
> that your wording seems to make a promise that I think shouldn't be
> made.
Agreed.
>
> >
> > > the key to accessing the normal contents of my mountns without
> > > changing my mountns, I need to chroot somewhere (like failfs) *and* I
> > > need to somehow prevent myself from getting an fd or a magic link back
> > > to the ordinary mountns contents. If I somehow get such an fd,
> > > preventing my from fchrooting to it doesn't seem to accomplish
> > > anything, since I could traverse the fd with openat, etc just as
> > > easily, or even fchdir to it and use relative paths.
> >
> > I think I generally agree with you. My main concern here is mostly about
> > changing very long-standing behavior here and making failfs special here
> > feels like it could easily misused.
>
> I don't think either of us are really suggesting making failfs
> special. I agree about the long-standing behavior.
>
> >
> > >
> > > My understanding of the exact workings of the mount tree is possibly
> > > not good enough to specify the semantics I think are correct quite
> > > exactly, but I think a decent approximation is that a task should be
> > > considered to be not chrooted if its root is at the top of its mount
> > > tree or is not in a mount tree at all.
> >
> > If we could altering this definition from the patchset I would be in
> > favor of it. I'm curious what your take is though.
> >
>
> I can't quite parse your question.
Maybe I should shunt everything I write through an LLM so it catches
grammatical nonsense.
It was basically just trying to say "let's not redesign
current_chrooted()" in this series - which I already did above.
> I have three sort-of-concrete proposals:
The best proposals...
> (a) current_chrooted returns false if follow_dotdot starting at
> current root but with nd->root equal to the namespace root would
> succeed and return the current root. I think this is fairly solid.
> The main caveat I can think of is that someone could create a detached
> mount tree, chroot to its root, then fork and have the child:
>
> - drop privileges
> - set no_new_privs
> - chroot somewhere else
>
> then the parent or another privileged task mounts that tree somewhere,
> and the child could now access the parent of the mountpoint. Any
> actual exploit based on this seems farfetched.
It would also be easy to prevent unprivileged chroot()ing into detached
mount trees. They're easy to recognize and it's not a super common
use-case.
>
> (b) separate the concepts of fs-root-for-absolute-paths and
> fs-root-for-blocking-dotdot. IMO this actually makes sense but would
> would be a much more intrusive change. We would have
> fs->root_of_abspaths and fs->dotdot_blocking_root, and fchroot would
> only change root_of_abspaths unless a flag is set asking to change
> both. (And document that setting that flag is not advised and that
> one should create a detached mount tree instead for 90% of use cases.)
> And no_new_privs tasks would be allowed to freely change
> root_of_abspaths.
>
> I like (b) conceptually in the sense that I wish no one had ever
> invented chroot-for-security in the first place. I'm not sure I like
> it as an actual practical idea because it has a much larger scope.
> Also (a) is almost equivalent as long as no one ever sets the root to
> something that isn't the root of a mount tree, and it really would
> confuse some people if "/.." was not equivalent to "./".
Yeah, I'm not so excited about this part...
>
> (c) Add a bit to fs_struct called something like chroot_locked.
> chroot() sets the bit if it chroots anywhere other than "/" or its
> equivalent. fchroot does not set it unless a flag asking for it is
> set. Switching mount namespace clears the bit. If you have
> no_new_privs and don't have chroot_locked then you can chroot.
That sounds reasonable - the fchroot() extension is also a nice touch.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 12:49 ` [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot() Andy Lutomirski
2026-07-23 13:01 ` Christian Brauner
@ 2026-07-23 14:42 ` Jann Horn
1 sibling, 0 replies; 15+ messages in thread
From: Jann Horn @ 2026-07-23 14:42 UTC (permalink / raw)
To: Andy Lutomirski
Cc: Christian Brauner, linux-fsdevel, John Ericson, linux-api,
H. Peter Anvin, Kees Cook, Farid Zakaria, Alexander Viro,
Jan Kara, linux-kernel, Jonathan Corbet, linux-doc
On Thu, Jul 23, 2026 at 2:49 PM Andy Lutomirski <luto@kernel.org> wrote:
> On Thu, Jul 23, 2026 at 4:37 AM Christian Brauner <brauner@kernel.org> wrote:
> >
> > Once entered, failfs is a throw-away-the-key moment. The task is
> > considered chrooted so it cannot create user namespaces to regain
> > CAP_SYS_CHROOT. chroot()/fchroot() back out require CAP_SYS_CHROOT.
>
> I kind of alluded to this earlier, but I don't think we should promise
> this. In particular, I still think we should eventually allow a
> non-chrooted process with no_new_privs to chroot to any valid
> directory, and I think we should consider changing the definition of
FWIW, I kind of like that idea, but I think we need to look out for
how this interacts with path-based security checks and auditing.
Examples I can think of immediately are:
- bpf_path_d_path() is intended for BPF LSMs that do
security/auditing, but uses d_path() which stops at the chroot.
- audit_log_d_path() uses d_path()
These are already kinda broken in the face of unprivileged user
namespaces, but adding unprivileged arbitrary chroot() would break
them even more.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 14:07 ` Jann Horn
@ 2026-07-23 16:56 ` Andy Lutomirski
0 siblings, 0 replies; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-23 16:56 UTC (permalink / raw)
To: Jann Horn
Cc: Christian Brauner, linux-fsdevel, Andy Lutomirski, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
On Thu, Jul 23, 2026 at 7:09 AM Jann Horn <jannh@google.com> wrote:
>
> On Thu, Jul 23, 2026 at 1:30 PM Christian Brauner <brauner@kernel.org> wrote:
> > There's also some thought needed around shared fs_struct state. It's
> > obviously possible to chroot into failfs with a shared fs_struct if the
> > caller has CAP_SYS_CHROOT and shares the fs_struct or if the caller is
> > no_new_privs and shares the fs_struct. The non-chrooted-currently
> > requirement still applies.
>
> Having CAP_SYS_CHROOT and sharing the fs_struct should not be an issue
> because we already ensure that any tasks that share fs_struct are in
> the same user namespace, so CAP_SYS_CHROOT is implicitly also held
> relative to other users of the fs_struct. (In particular,
> userns_install() and unshare_fs() ensure this.) (Except if LSMs get
> involved with weird policy, I guess.) That's what makes the existing
> chroot() syscall safe.
>
> But yeah, the no_new_privs check I suggested is kind of a pain...
>
It's dorky, but we could say that your fs_struct must be non-shared
for unprivileged chroot to be allowed.
(I'm so mad at Docker for blocking *all* unshare calls in their
default policy. Not a showstopper, but it's extremely annoying. Yes,
I have an issue open that has been completely ignored.)
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 14:35 ` Christian Brauner
@ 2026-07-23 16:59 ` Andy Lutomirski
2026-07-27 3:30 ` John Ericson
0 siblings, 1 reply; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-23 16:59 UTC (permalink / raw)
To: Christian Brauner
Cc: Andy Lutomirski, linux-fsdevel, Jann Horn, John Ericson,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Alexander Viro, Jan Kara, linux-kernel, Jonathan Corbet,
linux-doc
On Thu, Jul 23, 2026 at 7:49 AM Christian Brauner <brauner@kernel.org> wrote:
>
> On 2026-07-23 06:50 -0700, Andy Lutomirski wrote:
> > On Thu, Jul 23, 2026 at 6:09 AM Christian Brauner <brauner@kernel.org> wrote:
> > >
> > > On Thu, Jul 23, 2026 at 05:49:06AM -0700, Andy Lutomirski wrote:
> > > > On Thu, Jul 23, 2026 at 4:37 AM Christian Brauner <brauner@kernel.org> wrote:
> > > > >
> > > > > Once entered, failfs is a throw-away-the-key moment. The task is
> > > > > considered chrooted so it cannot create user namespaces to regain
> > > > > CAP_SYS_CHROOT. chroot()/fchroot() back out require CAP_SYS_CHROOT.
> > > >
> > > > I kind of alluded to this earlier, but I don't think we should promise
> > > > this. In particular, I still think we should eventually allow a
> > > > non-chrooted process with no_new_privs to chroot to any valid
> > > > directory, and I think we should consider changing the definition of
> > > > current_chrooted() such that failfs-as-root would cause it to return
> > > > false. I'm also not sure why it's useful -- if I want to throw away
> > >
> > > I had considered that but introducing this change alongside this series
> > > felt too spicy for me.
> >
> > I think I agree. My only actual objection to your series as is is
> > that your wording seems to make a promise that I think shouldn't be
> > made.
>
> Agreed.
>
> >
> > >
> > > > the key to accessing the normal contents of my mountns without
> > > > changing my mountns, I need to chroot somewhere (like failfs) *and* I
> > > > need to somehow prevent myself from getting an fd or a magic link back
> > > > to the ordinary mountns contents. If I somehow get such an fd,
> > > > preventing my from fchrooting to it doesn't seem to accomplish
> > > > anything, since I could traverse the fd with openat, etc just as
> > > > easily, or even fchdir to it and use relative paths.
> > >
> > > I think I generally agree with you. My main concern here is mostly about
> > > changing very long-standing behavior here and making failfs special here
> > > feels like it could easily misused.
> >
> > I don't think either of us are really suggesting making failfs
> > special. I agree about the long-standing behavior.
> >
> > >
> > > >
> > > > My understanding of the exact workings of the mount tree is possibly
> > > > not good enough to specify the semantics I think are correct quite
> > > > exactly, but I think a decent approximation is that a task should be
> > > > considered to be not chrooted if its root is at the top of its mount
> > > > tree or is not in a mount tree at all.
> > >
> > > If we could altering this definition from the patchset I would be in
> > > favor of it. I'm curious what your take is though.
> > >
> >
> > I can't quite parse your question.
>
> Maybe I should shunt everything I write through an LLM so it catches
> grammatical nonsense.
>
> It was basically just trying to say "let's not redesign
> current_chrooted()" in this series - which I already did above.
>
> > I have three sort-of-concrete proposals:
>
> The best proposals...
>
> > (a) current_chrooted returns false if follow_dotdot starting at
> > current root but with nd->root equal to the namespace root would
> > succeed and return the current root. I think this is fairly solid.
> > The main caveat I can think of is that someone could create a detached
> > mount tree, chroot to its root, then fork and have the child:
> >
> > - drop privileges
> > - set no_new_privs
> > - chroot somewhere else
> >
> > then the parent or another privileged task mounts that tree somewhere,
> > and the child could now access the parent of the mountpoint. Any
> > actual exploit based on this seems farfetched.
>
> It would also be easy to prevent unprivileged chroot()ing into detached
> mount trees. They're easy to recognize and it's not a super common
> use-case.
In some sense chrooting into a detached mount tree is an entirely
reasonable operation. Make your favorite mount tree and chroot there
-- it's like mount-namespaces minus the namespace.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-23 16:59 ` Andy Lutomirski
@ 2026-07-27 3:30 ` John Ericson
2026-07-27 7:42 ` Andy Lutomirski
0 siblings, 1 reply; 15+ messages in thread
From: John Ericson @ 2026-07-27 3:30 UTC (permalink / raw)
To: Andy Lutomirski, Christian Brauner
Cc: linux-fsdevel, Jann Horn, linux-api, H. Peter Anvin, Kees Cook,
Farid Zakaria, Al Viro, Jan Kara, LKML, Jonathan Corbet,
linux-doc
Very happy to see this patch series appear; thanks Christian!
Also happy with where this specific thread is going. I will just add
that an additional flag in fs_struct to force RESOLVE_BENEATH in *all* path
lookup would also be nice. The combination of:
root = pwd = failfs;
chroot_locked = false;
always_resolve_beneath = true;
goes along way to getting Capsicum's "capability mode". Then we just
need to figure out things like more capabilities for directory file
descriptors per Andy's brainstorming thread thread (which I ought to get
to replying to).
John
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-27 3:30 ` John Ericson
@ 2026-07-27 7:42 ` Andy Lutomirski
2026-07-27 7:57 ` Andy Lutomirski
0 siblings, 1 reply; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-27 7:42 UTC (permalink / raw)
To: John Ericson
Cc: Andy Lutomirski, Christian Brauner, linux-fsdevel, Jann Horn,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria, Al Viro,
Jan Kara, LKML, Jonathan Corbet, linux-doc
> On Jul 27, 2026, at 5:30 AM, John Ericson <mail@johnericson.me> wrote:
>
> Very happy to see this patch series appear; thanks Christian!
>
> Also happy with where this specific thread is going. I will just add
> that an additional flag in fs_struct to force RESOLVE_BENEATH in *all* path
> lookup would also be nice. The combination of:
>
> root = pwd = failfs;
> chroot_locked = false;
> always_resolve_beneath = true;
>
> goes along way to getting Capsicum's "capability mode".
One thing I dislike about this is that it’s (approximately) a task mode. Why should an fd representing a capability represent one capability (a subtree) to one task but a different capability (dotdot) in a different task? Do we need to prevent unsharing a userns if this bit is set? Do we want to have to worry about any task that could potentially SCM_RIGHTS and fd to a task without that bit set? I prefer beneathness being an OFD property.
Note that the obvious giant holes in capsicum-without-a-mode (root and cwd) and their respective dotdots are fixed, quite cleanly, by this current patch set.
I also think it’s a bit of an ancient mistake that paths like /.. work at all instead of generating errors. Maybe a future beneath-only fd could also disallow trying to dotdot past the root, even for symlinks that contain dotdots. This isn’t just an aesthetic thing - if I have ~/foo/bar being a symlink to ../bar, I think it would be a mistake for a beneath-only OFD to ~/foo to have “bar” resolve successfully to the wrong place.
> Then we just
> need to figure out things like more capabilities for directory file
> descriptors per Andy's brainstorming thread thread (which I ought to get
> to replying to).
>
> John
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-27 7:42 ` Andy Lutomirski
@ 2026-07-27 7:57 ` Andy Lutomirski
2026-07-27 8:59 ` Christian Brauner
0 siblings, 1 reply; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-27 7:57 UTC (permalink / raw)
To: John Ericson
Cc: Andy Lutomirski, Christian Brauner, linux-fsdevel, Jann Horn,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria, Al Viro,
Jan Kara, LKML, Jonathan Corbet, linux-doc
> On Jul 27, 2026, at 9:43 AM, Andy Lutomirski <luto@amacapital.net> wrote:
>
>
>>
>> goes along way to getting Capsicum's "capability mode".
>
> One thing I dislike about this is that it’s (approximately) a task mode. Why should an fd representing a capability represent one capability (a subtree) to one task but a different capability (dotdot) in a different task? Do we need to prevent unsharing a userns if this bit is set? Do we want to have to worry about any task that could potentially SCM_RIGHTS and fd to a task without that bit set? I prefer beneathness being an OFD property.
Sigh, I keep thinking and typing OFD, and that won’t magically make it
be correct. All these privilege restrictions applied to a file or
path reference need to follow every user-controllable reference around
correctly. So if I fchdir or fchroot to a restricted OFD, my cwd or
root needs to preserve those restrictions, and reopening "/" or "." or
any other path (via root or cwd or openat etc) needs to respect the
restrictions as well. Putting it in struct path may or may not be a
good idea.
And, if we allow cloning a restricted reference to a subtree to mount
it, we need to make that work correctly too.
Off the top of my head, for a minimum viable product, we could
disallow cloning/mounting from a restricted reference. And if we want
to try delegation (as in my other braindump email), we could try
*only* allowing delegation via clones/mounts and not in an OFD or cwd
or root, since there are potentially mildly thorny or at least
unintuitive interactions between symlinks and privilege delegation,
especially if mounts can delegate. (And I think that delegation via
OFD/cwd/root but not via mount is so weak that there's almost no point
-- I want to delegate to a container, and that requires mounts.)
>
> Note that the obvious giant holes in capsicum-without-a-mode (root and cwd) and their respective dotdots are fixed, quite cleanly, by this current patch set.
>
> I also think it’s a bit of an ancient mistake that paths like /.. work at all instead of generating errors. Maybe a future beneath-only fd could also disallow trying to dotdot past the root, even for symlinks that contain dotdots. This isn’t just an aesthetic thing - if I have ~/foo/bar being a symlink to ../bar, I think it would be a mistake for a beneath-only OFD to ~/foo to have “bar” resolve successfully to the wrong place.
>
>> Then we just
>> need to figure out things like more capabilities for directory file
>> descriptors per Andy's brainstorming thread thread (which I ought to get
>> to replying to).
>>
>> John
>>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-27 7:57 ` Andy Lutomirski
@ 2026-07-27 8:59 ` Christian Brauner
2026-07-27 10:23 ` Andy Lutomirski
0 siblings, 1 reply; 15+ messages in thread
From: Christian Brauner @ 2026-07-27 8:59 UTC (permalink / raw)
To: Andy Lutomirski
Cc: John Ericson, Andy Lutomirski, Christian Brauner, linux-fsdevel,
Jann Horn, linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria,
Al Viro, Jan Kara, LKML, Jonathan Corbet, linux-doc
On 2026-07-27 00:57 -0700, Andy Lutomirski wrote:
> > On Jul 27, 2026, at 9:43 AM, Andy Lutomirski <luto@amacapital.net> wrote:
> >
> >
> >>
> >> goes along way to getting Capsicum's "capability mode".
> >
> > One thing I dislike about this is that it’s (approximately) a task mode. Why should an fd representing a capability represent one capability (a subtree) to one task but a different capability (dotdot) in a different task? Do we need to prevent unsharing a userns if this bit is set? Do we want to have to worry about any task that could potentially SCM_RIGHTS and fd to a task without that bit set? I prefer beneathness being an OFD property.
>
> Sigh, I keep thinking and typing OFD, and that won’t magically make it
> be correct. All these privilege restrictions applied to a file or
> path reference need to follow every user-controllable reference around
> correctly. So if I fchdir or fchroot to a restricted OFD, my cwd or
> root needs to preserve those restrictions, and reopening "/" or "." or
> any other path (via root or cwd or openat etc) needs to respect the
> restrictions as well. Putting it in struct path may or may not be a
> good idea.
I think making struct path carry more meaning than it does right now
will cause us endless amounts of subtle issues. I fear that change very
very much. That will have such a fundamental ripple effect across _all_
corners of the codebase that I'm very unwilling to be led down that
road. And I think Al had traditionally similar reservations as me. One
of the really thorny issues with the O_PATH upgrade masks was precisely
the struct path modification.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot()
2026-07-27 8:59 ` Christian Brauner
@ 2026-07-27 10:23 ` Andy Lutomirski
0 siblings, 0 replies; 15+ messages in thread
From: Andy Lutomirski @ 2026-07-27 10:23 UTC (permalink / raw)
To: Christian Brauner
Cc: John Ericson, Andy Lutomirski, linux-fsdevel, Jann Horn,
linux-api, H. Peter Anvin, Kees Cook, Farid Zakaria, Al Viro,
Jan Kara, LKML, Jonathan Corbet, linux-doc, Christian Brauner
> On Jul 27, 2026, at 10:59 AM, Christian Brauner <brauner@kernel.org> wrote:
>
> On 2026-07-27 00:57 -0700, Andy Lutomirski wrote:
>>>> On Jul 27, 2026, at 9:43 AM, Andy Lutomirski <luto@amacapital.net> wrote:
>>>
>>>
>>>>
>>>> goes along way to getting Capsicum's "capability mode".
>>>
>>> One thing I dislike about this is that it’s (approximately) a task mode. Why should an fd representing a capability represent one capability (a subtree) to one task but a different capability (dotdot) in a different task? Do we need to prevent unsharing a userns if this bit is set? Do we want to have to worry about any task that could potentially SCM_RIGHTS and fd to a task without that bit set? I prefer beneathness being an OFD property.
>>
>> Sigh, I keep thinking and typing OFD, and that won’t magically make it
>> be correct. All these privilege restrictions applied to a file or
>> path reference need to follow every user-controllable reference around
>> correctly. So if I fchdir or fchroot to a restricted OFD, my cwd or
>> root needs to preserve those restrictions, and reopening "/" or "." or
>> any other path (via root or cwd or openat etc) needs to respect the
>> restrictions as well. Putting it in struct path may or may not be a
>> good idea.
>
> I think making struct path carry more meaning than it does right now
> will cause us endless amounts of subtle issues. I fear that change very
> very much. That will have such a fundamental ripple effect across _all_
> corners of the codebase that I'm very unwilling to be led down that
> road. And I think Al had traditionally similar reservations as me. One
> of the really thorny issues with the O_PATH upgrade masks was precisely
> the struct path modification.
>
The alternative (I think) is to enumerate all the places in the kernel that a path (or file?) reference exists and would want to carry various restriction bits.
I can think of:
Resolution (nameidata?)
struct file
cwd
root
optionally, vfsmount
Is that it? If I open a directory with a no-dotdot restriction, what can I possibly do with it that needs to respect that restriction? Each such operation either needs to respect the restriction or fail outright. fchdir really ought to work. fchroot probably should, too. We could just not allow clone/mount (at first?). All the /proc links either start at cwd, root, or a struct file, right?
There are things like overlayfs layers, but failing an attempt to use a restrictive reference to a path as an overlayfs layer doesn’t seem to terrible to me.
I think that, logically, we could create a struct path_restrictions and add it to struct file and two copies to fs_struct. And we wire it up into nameidata. And we carefully make every operation that doesn’t understand this fail if there are restrictions, which could be some new nameidata flag meaning “I understand path restrictions” where any attempt to resolve a path starting at root or cwd or a struct file with restrictions that doesn’t set that flag will fail immediately without even trying to resolve the path.
My intuition is that the implementation wouldn’t be so bad and that people could chip away at the unsupported bits that annoy them over time.
—Andy
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-07-27 10:23 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20260723-work-failfs-v1-0-3f69b9a9e958@kernel.org>
[not found] ` <20260723-work-failfs-v1-4-3f69b9a9e958@kernel.org>
2026-07-23 12:49 ` [PATCH RFC 4/7] fs: support FD_FAILFS_ROOT in fchroot() Andy Lutomirski
2026-07-23 13:01 ` Christian Brauner
2026-07-23 13:50 ` Andy Lutomirski
2026-07-23 14:35 ` Christian Brauner
2026-07-23 16:59 ` Andy Lutomirski
2026-07-27 3:30 ` John Ericson
2026-07-27 7:42 ` Andy Lutomirski
2026-07-27 7:57 ` Andy Lutomirski
2026-07-27 8:59 ` Christian Brauner
2026-07-27 10:23 ` Andy Lutomirski
2026-07-23 14:42 ` Jann Horn
2026-07-23 14:07 ` Jann Horn
2026-07-23 16:56 ` Andy Lutomirski
2026-07-23 12:53 ` [PATCH RFC 0/7] fs: add failfs Andy Lutomirski
[not found] ` <20260723-work-failfs-v1-7-3f69b9a9e958@kernel.org>
2026-07-23 14:04 ` [PATCH RFC 7/7] Documentation: add failfs documentation Miquel Sabaté Solà
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox