linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] exec: load_script: Allow interpreter argument truncation
@ 2019-02-14  1:27 Kees Cook
  2019-02-14 16:08 ` Oleg Nesterov
  0 siblings, 1 reply; 3+ messages in thread
From: Kees Cook @ 2019-02-14  1:27 UTC (permalink / raw)
  To: Linus Torvalds
  Cc: Samuel Dionne-Riel, Richard Weinberger, LKML, Graham Christensen,
	Oleg Nesterov, Michal Hocko, Andrew Morton

While we want to make sure the kernel doesn't attempt to execute a
truncated interpreter path, we must allow the interpreter arguments to
be truncated. Perl, for example, will re-read the script itself to parse
arguments correctly.

This documents the parsing steps, and will fail to exec if the string was
truncated with neither an end-of-line nor any trailing whitespace.

Reported-by: Samuel Dionne-Riel <samuel@dionne-riel.com>
Fixes: 8099b047ecc4 ("exec: load_script: don't blindly truncate shebang string")
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 fs/binfmt_script.c | 37 +++++++++++++++++++++++++++++++------
 1 file changed, 31 insertions(+), 6 deletions(-)

diff --git a/fs/binfmt_script.c b/fs/binfmt_script.c
index d0078cbb718b..3db23528bb85 100644
--- a/fs/binfmt_script.c
+++ b/fs/binfmt_script.c
@@ -20,6 +20,7 @@ static int load_script(struct linux_binprm *bprm)
 	char *cp;
 	struct file *file;
 	int retval;
+	bool truncated = false, end_of_interp = false;
 
 	if ((bprm->buf[0] != '#') || (bprm->buf[1] != '!'))
 		return -ENOEXEC;
@@ -42,32 +43,56 @@ static int load_script(struct linux_binprm *bprm)
 	fput(bprm->file);
 	bprm->file = NULL;
 
+	/*
+	 * Truncating interpreter arguments is okay: the interpreter
+	 * can re-read the script to parse them on its own. Truncating
+	 * the interpreter path itself, though, is bad. Note truncation
+	 * here, and check for either newline or start of arguments
+	 * below.
+	 */
 	for (cp = bprm->buf+2;; cp++) {
-		if (cp >= bprm->buf + BINPRM_BUF_SIZE)
-			return -ENOEXEC;
-		if (!*cp || (*cp == '\n'))
+		if (cp == bprm->buf + BINPRM_BUF_SIZE - 1) {
+			truncated = true;
 			break;
+		}
+		if (!*cp || (*cp == '\n')) {
+			end_of_interp = true;
+			break;
+		}
 	}
 	*cp = '\0';
 
+	/* Truncate trailing whitespace */
 	while (cp > bprm->buf) {
 		cp--;
-		if ((*cp == ' ') || (*cp == '\t'))
+		if ((*cp == ' ') || (*cp == '\t')) {
+			end_of_interp = true;
 			*cp = '\0';
-		else
+		} else
 			break;
 	}
+	/* Skip leading whitespace */
 	for (cp = bprm->buf+2; (*cp == ' ') || (*cp == '\t'); cp++);
 	if (*cp == '\0')
 		return -ENOEXEC; /* No interpreter name found */
 	i_name = cp;
 	i_arg = NULL;
+	/*
+	 * Skip until end of string or finding whitespace which
+	 * signals the start of interpreter arguments.
+	 */
 	for ( ; *cp && (*cp != ' ') && (*cp != '\t'); cp++)
 		/* nothing */ ;
-	while ((*cp == ' ') || (*cp == '\t'))
+	/* Truncate and skip any whitespace in front of arguments */
+	while ((*cp == ' ') || (*cp == '\t')) {
+		end_of_interp = true;
 		*cp++ = '\0';
+	}
 	if (*cp)
 		i_arg = cp;
+	/* Fail exec if the name of the interpreter was cut off. */
+	if (truncated && !end_of_interp)
+		return -ENOEXEC;
 	/*
 	 * OK, we've parsed out the interpreter name and
 	 * (optional) argument.
-- 
2.17.1


-- 
Kees Cook

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] exec: load_script: Allow interpreter argument truncation
  2019-02-14  1:27 [PATCH] exec: load_script: Allow interpreter argument truncation Kees Cook
@ 2019-02-14 16:08 ` Oleg Nesterov
  2019-02-14 16:38   ` Kees Cook
  0 siblings, 1 reply; 3+ messages in thread
From: Oleg Nesterov @ 2019-02-14 16:08 UTC (permalink / raw)
  To: Kees Cook
  Cc: Linus Torvalds, Samuel Dionne-Riel, Richard Weinberger, LKML,
	Graham Christensen, Michal Hocko, Andrew Morton

On 02/13, Kees Cook wrote:
>
> While we want to make sure the kernel doesn't attempt to execute a
> truncated interpreter path, we must allow the interpreter arguments to
> be truncated. Perl, for example, will re-read the script itself to parse
> arguments correctly.

Heh. I still think that 8099b047ecc4 does the right thing.

But I can't argue with the fact that it caused the regression, so it should
be reverted.

> This documents the parsing steps, and will fail to exec if the string was
> truncated with neither an end-of-line nor any trailing whitespace.

You know, I have already spent 3 hours trying to write something simple and
clear, but failed. Still trying...

Nor I can really understand your fix ;) Will try to read it again, just one
question for now,

>  	for (cp = bprm->buf+2;; cp++) {
> -		if (cp >= bprm->buf + BINPRM_BUF_SIZE)
> -			return -ENOEXEC;
> -		if (!*cp || (*cp == '\n'))
> +		if (cp == bprm->buf + BINPRM_BUF_SIZE - 1) {
> +			truncated = true;

Off-by-one, no? "bprm->buf + BINPRM_BUF_SIZE - 1" is the very last char, it can
be '\n' or '\0', this should set end_of_interp.

>  			break;
> +		}
> +		if (!*cp || (*cp == '\n')) {
> +			end_of_interp = true;
> +			break;
> +		}

so unless I am totally confused you should move this block up before the
"bprm->buf + BINPRM_BUF_SIZE - 1" check or that check should use
"bprm->buf + BINPRM_BUF_SIZE".

No?

Oleg.


^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] exec: load_script: Allow interpreter argument truncation
  2019-02-14 16:08 ` Oleg Nesterov
@ 2019-02-14 16:38   ` Kees Cook
  0 siblings, 0 replies; 3+ messages in thread
From: Kees Cook @ 2019-02-14 16:38 UTC (permalink / raw)
  To: Oleg Nesterov
  Cc: Linus Torvalds, Samuel Dionne-Riel, Richard Weinberger, LKML,
	Graham Christensen, Michal Hocko, Andrew Morton

On Thu, Feb 14, 2019 at 8:08 AM Oleg Nesterov <oleg@redhat.com> wrote:
>
> On 02/13, Kees Cook wrote:
> >
> > While we want to make sure the kernel doesn't attempt to execute a
> > truncated interpreter path, we must allow the interpreter arguments to
> > be truncated. Perl, for example, will re-read the script itself to parse
> > arguments correctly.
>
> Heh. I still think that 8099b047ecc4 does the right thing.
>
> But I can't argue with the fact that it caused the regression, so it should
> be reverted.
>
> > This documents the parsing steps, and will fail to exec if the string was
> > truncated with neither an end-of-line nor any trailing whitespace.
>
> You know, I have already spent 3 hours trying to write something simple and
> clear, but failed. Still trying...

That's why I added comments too. It's kind of a weird bit of parsing,
and has to protect itself from lack of initial NUL-termination. :P

> Nor I can really understand your fix ;) Will try to read it again, just one
> question for now,
>
> >       for (cp = bprm->buf+2;; cp++) {
> > -             if (cp >= bprm->buf + BINPRM_BUF_SIZE)
> > -                     return -ENOEXEC;
> > -             if (!*cp || (*cp == '\n'))
> > +             if (cp == bprm->buf + BINPRM_BUF_SIZE - 1) {
> > +                     truncated = true;
>
> Off-by-one, no? "bprm->buf + BINPRM_BUF_SIZE - 1" is the very last char, it can
> be '\n' or '\0', this should set end_of_interp.

Ah yeah, this bails out one byte too early. As you suggestion, I
should move that test to after the !*cp || *cp == '\n' test, like so:

        for (cp = bprm->buf+2;; cp++) {
                if (!*cp || (*cp == '\n')) {
                        end_of_interp = true;
                        break;
                }
                if (cp == bprm->buf + BINPRM_BUF_SIZE - 1) {
                        truncated = true;
                        break;
                }
        }
        *cp = '\0';

> >                       break;
> > +             }
> > +             if (!*cp || (*cp == '\n')) {
> > +                     end_of_interp = true;
> > +                     break;
> > +             }
>
> so unless I am totally confused you should move this block up before the
> "bprm->buf + BINPRM_BUF_SIZE - 1" check or that check should use
> "bprm->buf + BINPRM_BUF_SIZE".
>
> No?

Moving the block is right, dropping the -1 would be lead to the
post-loop *cp = '\0' writing past the end of the buffer.

With this change, my tests (after gaining an extra byte of available
interp path name) still pass. I'll send a v2...

Thanks!

-- 
Kees Cook

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2019-02-14 16:38 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-02-14  1:27 [PATCH] exec: load_script: Allow interpreter argument truncation Kees Cook
2019-02-14 16:08 ` Oleg Nesterov
2019-02-14 16:38   ` Kees Cook

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).