From: Kees Cook <kees@kernel.org>
To: Mateusz Guzik <mjguzik@gmail.com>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>,
Jann Horn <jannh@google.com>,
Christian Brauner <brauner@kernel.org>,
Jorge Merlino <jorge.merlino@canonical.com>,
Alexander Viro <viro@zeniv.linux.org.uk>,
Thomas Gleixner <tglx@linutronix.de>,
Andy Lutomirski <luto@kernel.org>,
Sebastian Andrzej Siewior <bigeasy@linutronix.de>,
Andrew Morton <akpm@linux-foundation.org>,
linux-mm@kvack.org, linux-fsdevel@vger.kernel.org,
John Johansen <john.johansen@canonical.com>,
Paul Moore <paul@paul-moore.com>,
James Morris <jmorris@namei.org>,
"Serge E. Hallyn" <serge@hallyn.com>,
Stephen Smalley <stephen.smalley.work@gmail.com>,
Eric Paris <eparis@parisplace.org>,
Richard Haines <richard_c_haines@btinternet.com>,
Casey Schaufler <casey@schaufler-ca.com>,
Xin Long <lucien.xin@gmail.com>,
"David S. Miller" <davem@davemloft.net>,
Todd Kjos <tkjos@google.com>,
Ondrej Mosnacek <omosnace@redhat.com>,
Prashanth Prahlad <pprahlad@redhat.com>,
Micah Morton <mortonm@chromium.org>,
Fenghua Yu <fenghua.yu@intel.com>,
Andrei Vagin <avagin@gmail.com>,
linux-kernel@vger.kernel.org, apparmor@lists.ubuntu.com,
linux-security-module@vger.kernel.org, selinux@vger.kernel.org,
linux-hardening@vger.kernel.org, oleg@redhat.com
Subject: Re: [PATCH 1/2] fs/exec: Explicitly unshare fs_struct on exec
Date: Wed, 14 May 2025 08:42:22 -0700 [thread overview]
Message-ID: <202505140822.6AB755B6@keescook> (raw)
In-Reply-To: <CAGudoHH-Jn5_4qnLV3qwzjTi2ZgfmfaO0qVSWW5gqdqkvchnDQ@mail.gmail.com>
On Wed, May 14, 2025 at 02:03:31AM +0200, Mateusz Guzik wrote:
> On Wed, May 14, 2025 at 12:17 AM Eric W. Biederman
> <ebiederm@xmission.com> wrote:
> >
> > Jann Horn <jannh@google.com> writes:
> >
> > > On Tue, May 13, 2025 at 10:57 PM Kees Cook <kees@kernel.org> wrote:
> > >> On May 13, 2025 6:05:45 AM PDT, Mateusz Guzik <mjguzik@gmail.com> wrote:
> > >> >Here is my proposal: *deny* exec of suid/sgid binaries if fs_struct is
> > >> >shared. This will have to be checked for after the execing proc becomes
> > >> >single-threaded ofc.
> > >>
> > >> Unfortunately the above Chrome helper is setuid and uses CLONE_FS.
> > >
> > > Chrome first launches a setuid helper, and then the setuid helper does
> > > CLONE_FS. Mateusz's proposal would not impact this usecase.
> > >
> > > Mateusz is proposing to block the case where a process first does
> > > CLONE_FS, and *then* one of the processes sharing the fs_struct does a
> > > setuid execve(). Linux already downgrades such an execve() to be
> > > non-setuid, which probably means anyone trying to do this will get
> > > hard-to-understand problems. Mateusz' proposal would just turn this
> > > hard-to-debug edgecase, which already doesn't really work, into a
> > > clean error; I think that is a nice improvement even just from the
> > > UAPI standpoint.
> > >
> > > If this change makes it possible to clean up the kernel code a bit, even better.
> >
> > What has brought this to everyone's attention just now? This is
> > the second mention of this code path I have seen this week.
> >
>
> There is a syzkaller report concerning ->in_exec handling, for example:
> https://lore.kernel.org/all/67dc67f0.050a0220.25ae54.001f.GAE@google.com/#t
>
> [...]
> > It looks like most of the lsm's also test bprm->unsafe.
> >
> > So I imagine someone could very carefully separate the non-ptrace case
> > from the ptrace case but *shrug*.
> >
> > Perhaps:
> >
> > if ((is_setid || __cap_gained(permitted, new_old)) &&
> > ((bprm->unsafe & ~LSM_UNSAFE_PTRACE) ||
> > !ptracer_capable(current, new->user_ns))) {
> > + if (!(bprm->unsafe & LSM_UNSAFE_PTRACE)) {
> > + return -EPERM;
> > + }
> > /* downgrade; they get no more than they had, and maybe less */
> > if (!ns_capable(new->user_ns, CAP_SETUID) ||
> > (bprm->unsafe & LSM_UNSAFE_NO_NEW_PRIVS)) {
> > new->euid = new->uid;
> > new->egid = new->gid;
> > }
> > new->cap_permitted = cap_intersect(new->cap_permitted,
> > old->cap_permitted);
> > }
> >
> > If that is what you want that doesn't look to scary. I don't think
> > it simplifies anything about fs->in_exec. As fs->in_exec is set when
> > the processing calling exec is the only process that owns the fs_struct.
> > With fs->in_exec just being a flag that doesn't allow another thread
> > to call fork and start sharing the fs_struct during exec.
> >
> > *Shrug*
> >
> > I don't see why anyone would care. It is just a very silly corner case.
>
> Well I don't see how ptrace factors into any of this, apart from being
> a different case of ignoring suid/sgid.
I actually think we might want to expand the above bit of logic to use
an explicit tests of each LSM_UNSAFE case -- the merged
logic is very difficult to read currently. Totally untested expansion,
if I'm reading everything correctly:
if (bprm->unsafe &&
(is_setid || __cap_gained(permitted, new_old))) {
bool limit_caps = false;
bool strip_eid = false;
unsigned int unsafe = bprm->unsafe;
/* Check each bit */
if (unsafe & LSM_UNSAFE_PTRACE) {
if (!ptracer_capable(current, new->user_ns))
limit_caps = true;
unsafe &= ~LSM_UNSAFE_PTRACE;
}
if (unsafe & LSM_UNSAFE_SHARE) {
limit_caps = true;
if (!ns_capable(new->user_ns, CAP_SETUID))
strip_eid = true;
unsafe &= ~LSM_UNSAFE_SHARE;
}
if (unsafe & LSM_UNSAFE_NO_NEW_PRIVS) {
limit_caps = true;
if (!ns_capable(new->user_ns, CAP_SETUID))
strip_eid = true;
unsafe &= ~LSM_UNSAFE_NO_NEW_PRIVS;
}
if (WARN(unsafe, "Unhandled LSM_UNSAFE flag: %u?!\n", unsafe))
return -EINVAL;
if (limit_caps) {
new->cap_permitted = cap_intersect(new->cap_permitted,
old->cap_permitted);
}
if (strip_eid) {
new->euid = new->uid;
new->egid = new->gid;
}
}
> I can agree the suid/sgid situation vs CLONE_FS is a silly corner
> case, but one which needs to be handled for security reasons and which
> currently has weirdly convoluted code to do it.
>
> The intent behind my proposal is very much to get the crapper out of
> the way in a future-proof and simple manner.
>
> In check_unsafe_exec() you can find a nasty loop over threads in the
> group to find out if the fs struct is used by anyone outside of said
> group. Since fs struct users are not explicitly tracked and any of
> them can have different creds than the current thread, the kernel opts
> to ignore suid/sgid if there are extra users found (for security
> reasons). The loop depends on no new threads showing up as the list is
> being walked, to that end copy_fs() can transiently return an error if
> it spots ->in_exec.
>
> The >in_exec field is used as a boolean/flag, but parallel execs using
> the same fs struct from different thread groups don't look serialized.
> This is supposed to be fine as in this case ->in_exec is not getting
> set to begin with, but it gets unconditionally unset on all execs.
>
> And so on. It's all weird af.
100% :)
--
Kees Cook
next prev parent reply other threads:[~2025-05-14 15:42 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2022-10-06 8:27 [PATCH 0/2] fs/exec: Explicitly unshare fs_struct on exec Kees Cook
2022-10-06 8:27 ` [PATCH 1/2] " Kees Cook
2022-10-06 9:05 ` Christian Brauner
2022-10-06 10:48 ` David Laight
2022-10-06 14:13 ` Jann Horn
2022-10-06 15:25 ` Kees Cook
2022-10-06 15:35 ` Jann Horn
2025-05-13 13:05 ` Mateusz Guzik
2025-05-13 15:29 ` Eric W. Biederman
2025-05-13 20:57 ` Kees Cook
2025-05-13 21:09 ` Jann Horn
2025-05-13 22:16 ` Eric W. Biederman
2025-05-14 0:03 ` Mateusz Guzik
2025-05-14 15:33 ` Eric W. Biederman
2025-05-14 15:42 ` Kees Cook [this message]
2025-05-15 16:48 ` Eric W. Biederman
2025-05-13 23:15 ` Kees Cook
2022-10-14 3:18 ` Andy Lutomirski
2022-10-14 3:54 ` Kees Cook
2022-10-14 15:35 ` Jann Horn
2022-10-18 7:09 ` Kees Cook
2022-10-18 11:19 ` Jann Horn
2022-10-14 22:03 ` David Laight
2022-11-28 17:49 ` Eric W. Biederman
2022-10-06 8:27 ` [PATCH 2/2] exec: Remove LSM_UNSAFE_SHARE Kees Cook
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=202505140822.6AB755B6@keescook \
--to=kees@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=apparmor@lists.ubuntu.com \
--cc=avagin@gmail.com \
--cc=bigeasy@linutronix.de \
--cc=brauner@kernel.org \
--cc=casey@schaufler-ca.com \
--cc=davem@davemloft.net \
--cc=ebiederm@xmission.com \
--cc=eparis@parisplace.org \
--cc=fenghua.yu@intel.com \
--cc=jannh@google.com \
--cc=jmorris@namei.org \
--cc=john.johansen@canonical.com \
--cc=jorge.merlino@canonical.com \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-hardening@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=linux-security-module@vger.kernel.org \
--cc=lucien.xin@gmail.com \
--cc=luto@kernel.org \
--cc=mjguzik@gmail.com \
--cc=mortonm@chromium.org \
--cc=oleg@redhat.com \
--cc=omosnace@redhat.com \
--cc=paul@paul-moore.com \
--cc=pprahlad@redhat.com \
--cc=richard_c_haines@btinternet.com \
--cc=selinux@vger.kernel.org \
--cc=serge@hallyn.com \
--cc=stephen.smalley.work@gmail.com \
--cc=tglx@linutronix.de \
--cc=tkjos@google.com \
--cc=viro@zeniv.linux.org.uk \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).