From: Kees Cook <kees@kernel.org>
To: Tycho Andersen <tycho@tycho.pizza>
Cc: "Zbigniew Jędrzejewski-Szmek" <zbyszek@in.waw.pl>,
"Eric W. Biederman" <ebiederm@xmission.com>,
"Alexander Viro" <viro@zeniv.linux.org.uk>,
"Christian Brauner" <brauner@kernel.org>,
"Jan Kara" <jack@suse.cz>, "Jeff Layton" <jlayton@kernel.org>,
"Chuck Lever" <chuck.lever@oracle.com>,
"Alexander Aring" <alex.aring@gmail.com>,
linux-fsdevel@vger.kernel.org, linux-mm@kvack.org,
linux-kernel@vger.kernel.org,
"Tycho Andersen" <tandersen@netflix.com>,
"Aleksa Sarai" <cyphar@cyphar.com>
Subject: Re: [RFC] exec: add a flag for "reasonable" execveat() comm
Date: Thu, 17 Oct 2024 08:47:03 -0700 [thread overview]
Message-ID: <202410170840.8E974776@keescook> (raw)
In-Reply-To: <ZxEgg+CEnvIHJJ4q@tycho.pizza>
On Thu, Oct 17, 2024 at 08:34:43AM -0600, Tycho Andersen wrote:
> On Mon, Oct 14, 2024 at 02:13:32PM -0700, Kees Cook wrote:
> > On Wed, Oct 09, 2024 at 08:41:31AM -0600, Tycho Andersen wrote:
> > > +static int bprm_add_fixup_comm(struct linux_binprm *bprm, struct user_arg_ptr argv)
> > > +{
> > > + const char __user *p = get_user_arg_ptr(argv, 0);
> > > +
> > > + /*
> > > + * In keeping with the logic in do_execveat_common(), we say p == NULL
> > > + * => "" for comm.
> > > + */
> > > + if (!p) {
> > > + bprm->argv0 = kstrdup("", GFP_KERNEL);
> > > + return 0;
> > > + }
> > > +
> > > + bprm->argv0 = strndup_user(p, MAX_ARG_STRLEN);
> > > + if (bprm->argv0)
> > > + return 0;
> > > +
> > > + return -EFAULT;
> > > +}
> >
> > I'd rather this logic got done in copy_strings() and to avoid duplicating
> > a copy for all exec users. I think it should be possible to just do
> > this, to find the __user char *:
> >
> > diff --git a/fs/exec.c b/fs/exec.c
> > index 77364806b48d..e12fd706f577 100644
> > --- a/fs/exec.c
> > +++ b/fs/exec.c
> > @@ -642,6 +642,8 @@ static int copy_strings(int argc, struct user_arg_ptr argv,
> > goto out;
> > }
> > }
> > + if (argc == 0)
> > + bprm->argv0 = str;
> > }
> > ret = 0;
> > out:
>
> Isn't str here a __user? We want a kernel string for setting comm, so
> I guess kaddr+offset? But that's not mapped any more...
Yes, but it'll be valid __user addr in the new process. (IIUC)
> > Once we get to begin_new_exec(), only if we need to do the work (fdpath
> > set), then we can do the strndup_user() instead of making every exec
> > hold a copy regardless of whether it will be needed.
>
> What happens if that allocation fails? begin_new_exec() says it is the
> point of no return, so we would just swallow the exec? Or have
> mysteriously inconsistent behavior?
If we can't alloc a string in begin_new_exec() we're going to have much
later problems, so yeah, I'm fine with it failing there.
> I think we could check ->fdpath in the bprm_add_fixup_comm() above,
> and only do the allocation when really necessary. I should have done
> that in the above version, which would have made the comment about
> checking fdpath even somewhat true :)
But to keep this more readable, I do like your version below, with some
notes.
>
> Something like the below?
>
> Tycho
>
>
>
> diff --git a/fs/exec.c b/fs/exec.c
> index dad402d55681..7ec0bbfbc3c3 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -1416,7 +1416,16 @@ int begin_new_exec(struct linux_binprm * bprm)
> set_dumpable(current->mm, SUID_DUMP_USER);
>
> perf_event_exec();
> - __set_task_comm(me, kbasename(bprm->filename), true);
> +
> + /*
> + * If argv0 was set, execveat() made up a path that will
> + * probably not be useful to admins running ps or similar.
> + * Let's fix it up to be something reasonable.
> + */
> + if (bprm->argv0)
> + __set_task_comm(me, kbasename(bprm->argv0), true);
> + else
> + __set_task_comm(me, kbasename(bprm->filename), true);
>
> /* An exec changes our domain. We are no longer part of the thread
> group */
> @@ -1566,9 +1575,36 @@ static void free_bprm(struct linux_binprm *bprm)
> if (bprm->interp != bprm->filename)
> kfree(bprm->interp);
> kfree(bprm->fdpath);
> + kfree(bprm->argv0);
> kfree(bprm);
> }
>
> +static int bprm_add_fixup_comm(struct linux_binprm *bprm, struct user_arg_ptr argv)
> +{
> + const char __user *p = get_user_arg_ptr(argv, 0);
To keep this const but not call get_user_arg_ptr() before the fdpath
check, how about externalizing it. See further below...
> +
> + /*
> + * If this isn't an execveat(), we don't need to fix up the command.
> + */
> + if (!bprm->fdpath)
> + return 0;
> +
> + /*
> + * In keeping with the logic in do_execveat_common(), we say p == NULL
> + * => "" for comm.
> + */
> + if (!p) {
> + bprm->argv0 = kstrdup("", GFP_KERNEL);
Do we want an empty argv0, though? Shouldn't an empty fall back to
fdpath?
> + return 0;
> + }
> +
> + bprm->argv0 = strndup_user(p, MAX_ARG_STRLEN);
> + if (bprm->argv0)
> + return 0;
> +
> + return -EFAULT;
> +}
> +
> static struct linux_binprm *alloc_bprm(int fd, struct filename *filename, int flags)
> {
> struct linux_binprm *bprm;
> @@ -1975,6 +2011,10 @@ static int do_execveat_common(int fd, struct filename *filename,
> goto out_ret;
> }
>
> + retval = bprm_add_fixup_comm(bprm, argv);
> + if (retval != 0)
> + goto out_free;
How about:
if (unlikely(bprm->fdpath)) {
retval = bprm_add_fixup_comm(bprm, argv);
if (retval != 0)
goto out_free;
}
with the fdpath removed from bprm_add_fixup_comm()?
> +
> retval = count(argv, MAX_ARG_STRINGS);
> if (retval == 0)
> pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
--
Kees Cook
next prev parent reply other threads:[~2024-10-17 15:47 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-09-24 14:10 [RFC] exec: add a flag for "reasonable" execveat() comm Tycho Andersen
2024-09-24 17:39 ` Eric W. Biederman
2024-09-24 21:37 ` Kees Cook
2024-09-24 22:59 ` Tycho Andersen
2024-09-25 13:12 ` Eric W. Biederman
2024-09-25 15:50 ` Aleksa Sarai
2024-09-25 21:20 ` Tycho Andersen
2024-09-26 2:09 ` Eric W. Biederman
2024-09-27 14:07 ` Tycho Andersen
2024-09-27 14:43 ` Eric W. Biederman
2024-09-27 14:56 ` Tycho Andersen
2024-09-27 15:38 ` Eric W. Biederman
2024-10-02 14:34 ` Zbigniew Jędrzejewski-Szmek
2024-10-09 14:41 ` Tycho Andersen
2024-10-14 21:13 ` Kees Cook
2024-10-17 14:34 ` Tycho Andersen
2024-10-17 15:47 ` Kees Cook [this message]
2024-10-17 20:38 ` Tycho Andersen
2024-09-25 8:31 ` Christian Brauner
2024-09-25 13:18 ` Eric W. Biederman
2024-09-25 14:53 ` Christian Brauner
-- strict thread matches above, loose matches on Subject: below --
2024-09-25 16:44 Alexey Dobriyan
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=202410170840.8E974776@keescook \
--to=kees@kernel.org \
--cc=alex.aring@gmail.com \
--cc=brauner@kernel.org \
--cc=chuck.lever@oracle.com \
--cc=cyphar@cyphar.com \
--cc=ebiederm@xmission.com \
--cc=jack@suse.cz \
--cc=jlayton@kernel.org \
--cc=linux-fsdevel@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=tandersen@netflix.com \
--cc=tycho@tycho.pizza \
--cc=viro@zeniv.linux.org.uk \
--cc=zbyszek@in.waw.pl \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.