linux-hardening.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] exec: Force single empty string when argv is empty
@ 2022-02-01  0:09 Kees Cook
  2022-02-01  1:00 ` Ariadne Conill
                   ` (4 more replies)
  0 siblings, 5 replies; 10+ messages in thread
From: Kees Cook @ 2022-02-01  0:09 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Kees Cook, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Rich Felker, Eric Biederman, Alexander Viro,
	linux-fsdevel, stable, linux-kernel, linux-hardening

Quoting[1] Ariadne Conill:

"In several other operating systems, it is a hard requirement that the
second argument to execve(2) be the name of a program, thus prohibiting
a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
but it is not an explicit requirement[2]:

    The argument arg0 should point to a filename string that is
    associated with the process being started by one of the exec
    functions.
...
Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
but there was no consensus to support fixing this issue then.
Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
of this bug in a shellcode, we can reconsider.

This issue is being tracked in the KSPP issue tracker[5]."

While the initial code searches[6][7] turned up what appeared to be
mostly corner case tests, trying to that just reject argv == NULL
(or an immediately terminated pointer list) quickly started tripping[8]
existing userspace programs.

The next best approach is forcing a single empty string into argv and
adjusting argc to match. The number of programs depending on argc == 0
seems a smaller set than those calling execve with a NULL argv.

Account for the additional stack space in bprm_stack_limits(). Inject an
empty string when argc == 0 (and set argc = 1). Warn about the case so
userspace has some notice about the change:

    process './argc0' launched './argc0' with NULL argv: empty string added

Additionally WARN() and reject NULL argv usage for kernel threads.

[1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
[2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
[3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
[4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
[5] https://github.com/KSPP/linux/issues/176
[6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
[7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
[8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/

Reported-by: Ariadne Conill <ariadne@dereferenced.org>
Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
Cc: Matthew Wilcox <willy@infradead.org>
Cc: Christian Brauner <brauner@kernel.org>
Cc: Rich Felker <dalias@libc.org>
Cc: Eric Biederman <ebiederm@xmission.com>
Cc: Alexander Viro <viro@zeniv.linux.org.uk>
Cc: linux-fsdevel@vger.kernel.org
Cc: stable@vger.kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>
---
 fs/exec.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/fs/exec.c b/fs/exec.c
index 79f2c9483302..bbf3aadf7ce1 100644
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
 	 * the stack. They aren't stored until much later when we can't
 	 * signal to the parent that the child has run out of stack space.
 	 * Instead, calculate it here so it's possible to fail gracefully.
+	 *
+	 * In the case of argc = 0, make sure there is space for adding a
+	 * empty string (which will bump argc to 1), to ensure confused
+	 * userspace programs don't start processing from argv[1], thinking
+	 * argc can never be 0, to keep them from walking envp by accident.
+	 * See do_execveat_common().
 	 */
-	ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
+	ptr_size = (min(bprm->argc, 1) + bprm->envc) * sizeof(void *);
 	if (limit <= ptr_size)
 		return -E2BIG;
 	limit -= ptr_size;
@@ -1897,6 +1903,9 @@ static int do_execveat_common(int fd, struct filename *filename,
 	}
 
 	retval = count(argv, MAX_ARG_STRINGS);
+	if (retval == 0)
+		pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
+			     current->comm, bprm->filename);
 	if (retval < 0)
 		goto out_free;
 	bprm->argc = retval;
@@ -1923,6 +1932,19 @@ static int do_execveat_common(int fd, struct filename *filename,
 	if (retval < 0)
 		goto out_free;
 
+	/*
+	 * When argv is empty, add an empty string ("") as argv[0] to
+	 * ensure confused userspace programs that start processing
+	 * from argv[1] won't end up walking envp. See also
+	 * bprm_stack_limits().
+	 */
+	if (bprm->argc == 0) {
+		retval = copy_string_kernel("", bprm);
+		if (retval < 0)
+			goto out_free;
+		bprm->argc = 1;
+	}
+
 	retval = bprm_execve(bprm, fd, filename, flags);
 out_free:
 	free_bprm(bprm);
@@ -1951,6 +1973,8 @@ int kernel_execve(const char *kernel_filename,
 	}
 
 	retval = count_strings_kernel(argv);
+	if (WARN_ON_ONCE(retval == 0))
+		retval = -EINVAL;
 	if (retval < 0)
 		goto out_free;
 	bprm->argc = retval;
-- 
2.30.2


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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01  0:09 [PATCH] exec: Force single empty string when argv is empty Kees Cook
@ 2022-02-01  1:00 ` Ariadne Conill
  2022-02-01  2:00 ` Andy Lutomirski
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Ariadne Conill @ 2022-02-01  1:00 UTC (permalink / raw)
  To: Kees Cook
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Rich Felker, Eric Biederman, Alexander Viro,
	linux-fsdevel, stable, linux-kernel, linux-hardening

Hi,

On Mon, 31 Jan 2022, Kees Cook wrote:

> Quoting[1] Ariadne Conill:
>
> "In several other operating systems, it is a hard requirement that the
> second argument to execve(2) be the name of a program, thus prohibiting
> a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
> but it is not an explicit requirement[2]:
>
>    The argument arg0 should point to a filename string that is
>    associated with the process being started by one of the exec
>    functions.
> ...
> Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
> but there was no consensus to support fixing this issue then.
> Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
> of this bug in a shellcode, we can reconsider.
>
> This issue is being tracked in the KSPP issue tracker[5]."
>
> While the initial code searches[6][7] turned up what appeared to be
> mostly corner case tests, trying to that just reject argv == NULL
> (or an immediately terminated pointer list) quickly started tripping[8]
> existing userspace programs.

Yes, it's a shame this is the case, but we do what we have to do, I guess 
:)

>
> The next best approach is forcing a single empty string into argv and
> adjusting argc to match. The number of programs depending on argc == 0
> seems a smaller set than those calling execve with a NULL argv.
>
> Account for the additional stack space in bprm_stack_limits(). Inject an
> empty string when argc == 0 (and set argc = 1). Warn about the case so
> userspace has some notice about the change:
>
>    process './argc0' launched './argc0' with NULL argv: empty string added
>
> Additionally WARN() and reject NULL argv usage for kernel threads.
>
> [1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
> [3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
> [4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
> [5] https://github.com/KSPP/linux/issues/176
> [6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
> [7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
> [8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/
>
> Reported-by: Ariadne Conill <ariadne@dereferenced.org>
> Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
> Cc: Matthew Wilcox <willy@infradead.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Eric Biederman <ebiederm@xmission.com>
> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
> Cc: linux-fsdevel@vger.kernel.org
> Cc: stable@vger.kernel.org
> Signed-off-by: Kees Cook <keescook@chromium.org>

In terms of going with this approach as an alternative verses my original 
patch,

Acked-by: Ariadne Conill <ariadne@dereferenced.org>

Ariadne

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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01  0:09 [PATCH] exec: Force single empty string when argv is empty Kees Cook
  2022-02-01  1:00 ` Ariadne Conill
@ 2022-02-01  2:00 ` Andy Lutomirski
  2022-02-01  9:17 ` David Laight
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 10+ messages in thread
From: Andy Lutomirski @ 2022-02-01  2:00 UTC (permalink / raw)
  To: Kees Cook, Andrew Morton
  Cc: Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Rich Felker, Eric Biederman, Alexander Viro,
	linux-fsdevel, stable, linux-kernel, linux-hardening, Linux API

On 1/31/22 16:09, Kees Cook wrote:
> Quoting[1] Ariadne Conill:
> 
> "In several other operating systems, it is a hard requirement that the
> second argument to execve(2) be the name of a program, thus prohibiting
> a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
> but it is not an explicit requirement[2]:
> 
>      The argument arg0 should point to a filename string that is
>      associated with the process being started by one of the exec
>      functions.
> ...
> Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
> but there was no consensus to support fixing this issue then.
> Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
> of this bug in a shellcode, we can reconsider.
> 
> This issue is being tracked in the KSPP issue tracker[5]."
> 
> While the initial code searches[6][7] turned up what appeared to be
> mostly corner case tests, trying to that just reject argv == NULL
> (or an immediately terminated pointer list) quickly started tripping[8]
> existing userspace programs.
> 
> The next best approach is forcing a single empty string into argv and
> adjusting argc to match. The number of programs depending on argc == 0
> seems a smaller set than those calling execve with a NULL argv.
> 
> Account for the additional stack space in bprm_stack_limits(). Inject an
> empty string when argc == 0 (and set argc = 1). Warn about the case so
> userspace has some notice about the change:
> 
>      process './argc0' launched './argc0' with NULL argv: empty string added
> 
> Additionally WARN() and reject NULL argv usage for kernel threads.
> 
> [1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
> [3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
> [4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
> [5] https://github.com/KSPP/linux/issues/176
> [6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
> [7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
> [8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/

Acked-by: Andy Lutomirski <luto@kernel.org>

and cc-ing linux-api.

I agree that this should be done regardless of any security context change.

> 
> Reported-by: Ariadne Conill <ariadne@dereferenced.org>
> Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
> Cc: Matthew Wilcox <willy@infradead.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Eric Biederman <ebiederm@xmission.com>
> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
> Cc: linux-fsdevel@vger.kernel.org
> Cc: stable@vger.kernel.org
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>   fs/exec.c | 26 +++++++++++++++++++++++++-
>   1 file changed, 25 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/exec.c b/fs/exec.c
> index 79f2c9483302..bbf3aadf7ce1 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
>   	 * the stack. They aren't stored until much later when we can't
>   	 * signal to the parent that the child has run out of stack space.
>   	 * Instead, calculate it here so it's possible to fail gracefully.
> +	 *
> +	 * In the case of argc = 0, make sure there is space for adding a
> +	 * empty string (which will bump argc to 1), to ensure confused
> +	 * userspace programs don't start processing from argv[1], thinking
> +	 * argc can never be 0, to keep them from walking envp by accident.
> +	 * See do_execveat_common().
>   	 */
> -	ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
> +	ptr_size = (min(bprm->argc, 1) + bprm->envc) * sizeof(void *);
>   	if (limit <= ptr_size)
>   		return -E2BIG;
>   	limit -= ptr_size;
> @@ -1897,6 +1903,9 @@ static int do_execveat_common(int fd, struct filename *filename,
>   	}
>   
>   	retval = count(argv, MAX_ARG_STRINGS);
> +	if (retval == 0)
> +		pr_warn_once("process '%s' launched '%s' with NULL argv: empty string added\n",
> +			     current->comm, bprm->filename);
>   	if (retval < 0)
>   		goto out_free;
>   	bprm->argc = retval;
> @@ -1923,6 +1932,19 @@ static int do_execveat_common(int fd, struct filename *filename,
>   	if (retval < 0)
>   		goto out_free;
>   
> +	/*
> +	 * When argv is empty, add an empty string ("") as argv[0] to
> +	 * ensure confused userspace programs that start processing
> +	 * from argv[1] won't end up walking envp. See also
> +	 * bprm_stack_limits().
> +	 */
> +	if (bprm->argc == 0) {
> +		retval = copy_string_kernel("", bprm);
> +		if (retval < 0)
> +			goto out_free;
> +		bprm->argc = 1;
> +	}
> +
>   	retval = bprm_execve(bprm, fd, filename, flags);
>   out_free:
>   	free_bprm(bprm);
> @@ -1951,6 +1973,8 @@ int kernel_execve(const char *kernel_filename,
>   	}
>   
>   	retval = count_strings_kernel(argv);
> +	if (WARN_ON_ONCE(retval == 0))
> +		retval = -EINVAL;
>   	if (retval < 0)
>   		goto out_free;
>   	bprm->argc = retval;


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

* RE: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01  0:09 [PATCH] exec: Force single empty string when argv is empty Kees Cook
  2022-02-01  1:00 ` Ariadne Conill
  2022-02-01  2:00 ` Andy Lutomirski
@ 2022-02-01  9:17 ` David Laight
  2022-02-02 20:31   ` Kees Cook
  2022-02-01 13:22 ` Christian Brauner
  2022-02-01 14:53 ` Rich Felker
  4 siblings, 1 reply; 10+ messages in thread
From: David Laight @ 2022-02-01  9:17 UTC (permalink / raw)
  To: 'Kees Cook', Andrew Morton
  Cc: Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Rich Felker, Eric Biederman, Alexander Viro,
	linux-fsdevel, stable, linux-kernel, linux-hardening

From: Kees Cook
> Sent: 01 February 2022 00:10
...
> While the initial code searches[6][7] turned up what appeared to be
> mostly corner case tests, trying to that just reject argv == NULL
> (or an immediately terminated pointer list) quickly started tripping[8]
> existing userspace programs.
> 
> The next best approach is forcing a single empty string into argv and
> adjusting argc to match. The number of programs depending on argc == 0
> seems a smaller set than those calling execve with a NULL argv.

Has anyone considered using the pathname for argv[0]?
So converting:
	execl(path, NULL);
into:
	execl(path, path, NULL);

I've not spotted any such suggestion.

	David

-
Registered Address Lakeside, Bramley Road, Mount Farm, Milton Keynes, MK1 1PT, UK
Registration No: 1397386 (Wales)


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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01  0:09 [PATCH] exec: Force single empty string when argv is empty Kees Cook
                   ` (2 preceding siblings ...)
  2022-02-01  9:17 ` David Laight
@ 2022-02-01 13:22 ` Christian Brauner
  2022-02-01 14:53 ` Rich Felker
  4 siblings, 0 replies; 10+ messages in thread
From: Christian Brauner @ 2022-02-01 13:22 UTC (permalink / raw)
  To: Kees Cook
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Rich Felker, Eric Biederman, Alexander Viro, linux-fsdevel,
	stable, linux-kernel, linux-hardening

On Mon, Jan 31, 2022 at 04:09:47PM -0800, Kees Cook wrote:
> Quoting[1] Ariadne Conill:
> 
> "In several other operating systems, it is a hard requirement that the
> second argument to execve(2) be the name of a program, thus prohibiting
> a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
> but it is not an explicit requirement[2]:
> 
>     The argument arg0 should point to a filename string that is
>     associated with the process being started by one of the exec
>     functions.
> ...
> Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
> but there was no consensus to support fixing this issue then.
> Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
> of this bug in a shellcode, we can reconsider.
> 
> This issue is being tracked in the KSPP issue tracker[5]."
> 
> While the initial code searches[6][7] turned up what appeared to be
> mostly corner case tests, trying to that just reject argv == NULL
> (or an immediately terminated pointer list) quickly started tripping[8]
> existing userspace programs.
> 
> The next best approach is forcing a single empty string into argv and
> adjusting argc to match. The number of programs depending on argc == 0
> seems a smaller set than those calling execve with a NULL argv.
> 
> Account for the additional stack space in bprm_stack_limits(). Inject an
> empty string when argc == 0 (and set argc = 1). Warn about the case so
> userspace has some notice about the change:
> 
>     process './argc0' launched './argc0' with NULL argv: empty string added
> 
> Additionally WARN() and reject NULL argv usage for kernel threads.
> 
> [1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
> [3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
> [4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
> [5] https://github.com/KSPP/linux/issues/176
> [6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
> [7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
> [8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/
> 
> Reported-by: Ariadne Conill <ariadne@dereferenced.org>
> Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
> Cc: Matthew Wilcox <willy@infradead.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Eric Biederman <ebiederm@xmission.com>
> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
> Cc: linux-fsdevel@vger.kernel.org
> Cc: stable@vger.kernel.org
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---

Looks good,
Acked-by: Christian Brauner <brauner@kernel.org>

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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01  0:09 [PATCH] exec: Force single empty string when argv is empty Kees Cook
                   ` (3 preceding siblings ...)
  2022-02-01 13:22 ` Christian Brauner
@ 2022-02-01 14:53 ` Rich Felker
  2022-02-02 15:50   ` Kees Cook
  4 siblings, 1 reply; 10+ messages in thread
From: Rich Felker @ 2022-02-01 14:53 UTC (permalink / raw)
  To: Kees Cook
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Eric Biederman, Alexander Viro, linux-fsdevel,
	stable, linux-kernel, linux-hardening

On Mon, Jan 31, 2022 at 04:09:47PM -0800, Kees Cook wrote:
> Quoting[1] Ariadne Conill:
> 
> "In several other operating systems, it is a hard requirement that the
> second argument to execve(2) be the name of a program, thus prohibiting
> a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
> but it is not an explicit requirement[2]:
> 
>     The argument arg0 should point to a filename string that is
>     associated with the process being started by one of the exec
>     functions.
> ....
> Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
> but there was no consensus to support fixing this issue then.
> Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
> of this bug in a shellcode, we can reconsider.
> 
> This issue is being tracked in the KSPP issue tracker[5]."
> 
> While the initial code searches[6][7] turned up what appeared to be
> mostly corner case tests, trying to that just reject argv == NULL
> (or an immediately terminated pointer list) quickly started tripping[8]
> existing userspace programs.
> 
> The next best approach is forcing a single empty string into argv and
> adjusting argc to match. The number of programs depending on argc == 0
> seems a smaller set than those calling execve with a NULL argv.
> 
> Account for the additional stack space in bprm_stack_limits(). Inject an
> empty string when argc == 0 (and set argc = 1). Warn about the case so
> userspace has some notice about the change:
> 
>     process './argc0' launched './argc0' with NULL argv: empty string added
> 
> Additionally WARN() and reject NULL argv usage for kernel threads.
> 
> [1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
> [3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
> [4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
> [5] https://github.com/KSPP/linux/issues/176
> [6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
> [7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
> [8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/
> 
> Reported-by: Ariadne Conill <ariadne@dereferenced.org>
> Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
> Cc: Matthew Wilcox <willy@infradead.org>
> Cc: Christian Brauner <brauner@kernel.org>
> Cc: Rich Felker <dalias@libc.org>
> Cc: Eric Biederman <ebiederm@xmission.com>
> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
> Cc: linux-fsdevel@vger.kernel.org
> Cc: stable@vger.kernel.org
> Signed-off-by: Kees Cook <keescook@chromium.org>
> ---
>  fs/exec.c | 26 +++++++++++++++++++++++++-
>  1 file changed, 25 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/exec.c b/fs/exec.c
> index 79f2c9483302..bbf3aadf7ce1 100644
> --- a/fs/exec.c
> +++ b/fs/exec.c
> @@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
>  	 * the stack. They aren't stored until much later when we can't
>  	 * signal to the parent that the child has run out of stack space.
>  	 * Instead, calculate it here so it's possible to fail gracefully.
> +	 *
> +	 * In the case of argc = 0, make sure there is space for adding a
> +	 * empty string (which will bump argc to 1), to ensure confused
> +	 * userspace programs don't start processing from argv[1], thinking
> +	 * argc can never be 0, to keep them from walking envp by accident.
> +	 * See do_execveat_common().
>  	 */
> -	ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
> +	ptr_size = (min(bprm->argc, 1) + bprm->envc) * sizeof(void *);

From #musl:

<mixi> kees: shouldn't the min(bprm->argc, 1) be max(...) in your patch?

I'm pretty sure without fixing that, you're introducing a giant vuln
here. I believe this is the second time a patch attempting to fix this
non-vuln has proposed adding a new vuln...

Rich

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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01 14:53 ` Rich Felker
@ 2022-02-02 15:50   ` Kees Cook
  2022-02-02 17:12     ` Rich Felker
  0 siblings, 1 reply; 10+ messages in thread
From: Kees Cook @ 2022-02-02 15:50 UTC (permalink / raw)
  To: Rich Felker
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Eric Biederman, Alexander Viro, linux-fsdevel,
	stable, linux-kernel, linux-hardening



On February 1, 2022 6:53:25 AM PST, Rich Felker <dalias@libc.org> wrote:
>On Mon, Jan 31, 2022 at 04:09:47PM -0800, Kees Cook wrote:
>> Quoting[1] Ariadne Conill:
>> 
>> "In several other operating systems, it is a hard requirement that the
>> second argument to execve(2) be the name of a program, thus prohibiting
>> a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
>> but it is not an explicit requirement[2]:
>> 
>>     The argument arg0 should point to a filename string that is
>>     associated with the process being started by one of the exec
>>     functions.
>> ....
>> Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
>> but there was no consensus to support fixing this issue then.
>> Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
>> of this bug in a shellcode, we can reconsider.
>> 
>> This issue is being tracked in the KSPP issue tracker[5]."
>> 
>> While the initial code searches[6][7] turned up what appeared to be
>> mostly corner case tests, trying to that just reject argv == NULL
>> (or an immediately terminated pointer list) quickly started tripping[8]
>> existing userspace programs.
>> 
>> The next best approach is forcing a single empty string into argv and
>> adjusting argc to match. The number of programs depending on argc == 0
>> seems a smaller set than those calling execve with a NULL argv.
>> 
>> Account for the additional stack space in bprm_stack_limits(). Inject an
>> empty string when argc == 0 (and set argc = 1). Warn about the case so
>> userspace has some notice about the change:
>> 
>>     process './argc0' launched './argc0' with NULL argv: empty string added
>> 
>> Additionally WARN() and reject NULL argv usage for kernel threads.
>> 
>> [1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
>> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
>> [3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
>> [4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
>> [5] https://github.com/KSPP/linux/issues/176
>> [6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
>> [7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
>> [8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/
>> 
>> Reported-by: Ariadne Conill <ariadne@dereferenced.org>
>> Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
>> Cc: Matthew Wilcox <willy@infradead.org>
>> Cc: Christian Brauner <brauner@kernel.org>
>> Cc: Rich Felker <dalias@libc.org>
>> Cc: Eric Biederman <ebiederm@xmission.com>
>> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
>> Cc: linux-fsdevel@vger.kernel.org
>> Cc: stable@vger.kernel.org
>> Signed-off-by: Kees Cook <keescook@chromium.org>
>> ---
>>  fs/exec.c | 26 +++++++++++++++++++++++++-
>>  1 file changed, 25 insertions(+), 1 deletion(-)
>> 
>> diff --git a/fs/exec.c b/fs/exec.c
>> index 79f2c9483302..bbf3aadf7ce1 100644
>> --- a/fs/exec.c
>> +++ b/fs/exec.c
>> @@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
>>  	 * the stack. They aren't stored until much later when we can't
>>  	 * signal to the parent that the child has run out of stack space.
>>  	 * Instead, calculate it here so it's possible to fail gracefully.
>> +	 *
>> +	 * In the case of argc = 0, make sure there is space for adding a
>> +	 * empty string (which will bump argc to 1), to ensure confused
>> +	 * userspace programs don't start processing from argv[1], thinking
>> +	 * argc can never be 0, to keep them from walking envp by accident.
>> +	 * See do_execveat_common().
>>  	 */
>> -	ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
>> +	ptr_size = (min(bprm->argc, 1) + bprm->envc) * sizeof(void *);
>
>From #musl:
>
><mixi> kees: shouldn't the min(bprm->argc, 1) be max(...) in your patch?

Fix has already been sent, yup.

>I'm pretty sure without fixing that, you're introducing a giant vuln
>here.

I wouldn't say "giant", but yes, it weakened a defense in depth for avoiding high stack utilization.

> I believe this is the second time a patch attempting to fix this
>non-vuln has proposed adding a new vuln...

Mistakes happen, and that's why there is review and testing. Thank you for being part of the review process! :)

-Kees

-- 
Kees Cook

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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-02 15:50   ` Kees Cook
@ 2022-02-02 17:12     ` Rich Felker
  2022-02-02 19:03       ` Kees Cook
  0 siblings, 1 reply; 10+ messages in thread
From: Rich Felker @ 2022-02-02 17:12 UTC (permalink / raw)
  To: Kees Cook
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Eric Biederman, Alexander Viro, linux-fsdevel,
	stable, linux-kernel, linux-hardening

On Wed, Feb 02, 2022 at 07:50:42AM -0800, Kees Cook wrote:
> 
> 
> On February 1, 2022 6:53:25 AM PST, Rich Felker <dalias@libc.org> wrote:
> >On Mon, Jan 31, 2022 at 04:09:47PM -0800, Kees Cook wrote:
> >> Quoting[1] Ariadne Conill:
> >> 
> >> "In several other operating systems, it is a hard requirement that the
> >> second argument to execve(2) be the name of a program, thus prohibiting
> >> a scenario where argc < 1. POSIX 2017 also recommends this behaviour,
> >> but it is not an explicit requirement[2]:
> >> 
> >>     The argument arg0 should point to a filename string that is
> >>     associated with the process being started by one of the exec
> >>     functions.
> >> ....
> >> Interestingly, Michael Kerrisk opened an issue about this in 2008[3],
> >> but there was no consensus to support fixing this issue then.
> >> Hopefully now that CVE-2021-4034 shows practical exploitative use[4]
> >> of this bug in a shellcode, we can reconsider.
> >> 
> >> This issue is being tracked in the KSPP issue tracker[5]."
> >> 
> >> While the initial code searches[6][7] turned up what appeared to be
> >> mostly corner case tests, trying to that just reject argv == NULL
> >> (or an immediately terminated pointer list) quickly started tripping[8]
> >> existing userspace programs.
> >> 
> >> The next best approach is forcing a single empty string into argv and
> >> adjusting argc to match. The number of programs depending on argc == 0
> >> seems a smaller set than those calling execve with a NULL argv.
> >> 
> >> Account for the additional stack space in bprm_stack_limits(). Inject an
> >> empty string when argc == 0 (and set argc = 1). Warn about the case so
> >> userspace has some notice about the change:
> >> 
> >>     process './argc0' launched './argc0' with NULL argv: empty string added
> >> 
> >> Additionally WARN() and reject NULL argv usage for kernel threads.
> >> 
> >> [1] https://lore.kernel.org/lkml/20220127000724.15106-1-ariadne@dereferenced.org/
> >> [2] https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html
> >> [3] https://bugzilla.kernel.org/show_bug.cgi?id=8408
> >> [4] https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
> >> [5] https://github.com/KSPP/linux/issues/176
> >> [6] https://codesearch.debian.net/search?q=execve%5C+*%5C%28%5B%5E%2C%5D%2B%2C+*NULL&literal=0
> >> [7] https://codesearch.debian.net/search?q=execlp%3F%5Cs*%5C%28%5B%5E%2C%5D%2B%2C%5Cs*NULL&literal=0
> >> [8] https://lore.kernel.org/lkml/20220131144352.GE16385@xsang-OptiPlex-9020/
> >> 
> >> Reported-by: Ariadne Conill <ariadne@dereferenced.org>
> >> Reported-by: Michael Kerrisk <mtk.manpages@gmail.com>
> >> Cc: Matthew Wilcox <willy@infradead.org>
> >> Cc: Christian Brauner <brauner@kernel.org>
> >> Cc: Rich Felker <dalias@libc.org>
> >> Cc: Eric Biederman <ebiederm@xmission.com>
> >> Cc: Alexander Viro <viro@zeniv.linux.org.uk>
> >> Cc: linux-fsdevel@vger.kernel.org
> >> Cc: stable@vger.kernel.org
> >> Signed-off-by: Kees Cook <keescook@chromium.org>
> >> ---
> >>  fs/exec.c | 26 +++++++++++++++++++++++++-
> >>  1 file changed, 25 insertions(+), 1 deletion(-)
> >> 
> >> diff --git a/fs/exec.c b/fs/exec.c
> >> index 79f2c9483302..bbf3aadf7ce1 100644
> >> --- a/fs/exec.c
> >> +++ b/fs/exec.c
> >> @@ -495,8 +495,14 @@ static int bprm_stack_limits(struct linux_binprm *bprm)
> >>  	 * the stack. They aren't stored until much later when we can't
> >>  	 * signal to the parent that the child has run out of stack space.
> >>  	 * Instead, calculate it here so it's possible to fail gracefully.
> >> +	 *
> >> +	 * In the case of argc = 0, make sure there is space for adding a
> >> +	 * empty string (which will bump argc to 1), to ensure confused
> >> +	 * userspace programs don't start processing from argv[1], thinking
> >> +	 * argc can never be 0, to keep them from walking envp by accident.
> >> +	 * See do_execveat_common().
> >>  	 */
> >> -	ptr_size = (bprm->argc + bprm->envc) * sizeof(void *);
> >> +	ptr_size = (min(bprm->argc, 1) + bprm->envc) * sizeof(void *);
> >
> >From #musl:
> >
> ><mixi> kees: shouldn't the min(bprm->argc, 1) be max(...) in your patch?
> 
> Fix has already been sent, yup.
> 
> >I'm pretty sure without fixing that, you're introducing a giant vuln
> >here.
> 
> I wouldn't say "giant", but yes, it weakened a defense in depth for
> avoiding high stack utilization.

I thought it was deciding the amount of memory to allocate/reserve for
the arg slots, but based on the comment it looks like it's just a way
to fail early rather than making the new process image fault later if
they don't fit.

> > I believe this is the second time a patch attempting to fix this
> >non-vuln has proposed adding a new vuln...
> 
> Mistakes happen, and that's why there is review and testing. Thank
> you for being part of the review process! :)

I know, and I'm sorry for being a bit hostile over it, and for jumping
the gun about the severity. I just get frustrated when I see a rush to
make changes over an incidental part of a popularized vuln, with
disproportionate weight on "doing something" and not enough on being
careful.

Rich

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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-02 17:12     ` Rich Felker
@ 2022-02-02 19:03       ` Kees Cook
  0 siblings, 0 replies; 10+ messages in thread
From: Kees Cook @ 2022-02-02 19:03 UTC (permalink / raw)
  To: Rich Felker
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Eric Biederman, Alexander Viro, linux-fsdevel,
	stable, linux-kernel, linux-hardening

On Wed, Feb 02, 2022 at 12:12:19PM -0500, Rich Felker wrote:
> On Wed, Feb 02, 2022 at 07:50:42AM -0800, Kees Cook wrote:
> > On February 1, 2022 6:53:25 AM PST, Rich Felker <dalias@libc.org> wrote:
> > >From #musl:
> > >
> > ><mixi> kees: shouldn't the min(bprm->argc, 1) be max(...) in your patch?
> > 
> > Fix has already been sent, yup.
> > 
> > >I'm pretty sure without fixing that, you're introducing a giant vuln
> > >here.
> > 
> > I wouldn't say "giant", but yes, it weakened a defense in depth for
> > avoiding high stack utilization.
> 
> I thought it was deciding the amount of memory to allocate/reserve for
> the arg slots, but based on the comment it looks like it's just a way
> to fail early rather than making the new process image fault later if
> they don't fit.

Right.

> > > I believe this is the second time a patch attempting to fix this
> > >non-vuln has proposed adding a new vuln...
> > 
> > Mistakes happen, and that's why there is review and testing. Thank
> > you for being part of the review process! :)
> 
> I know, and I'm sorry for being a bit hostile over it, and for jumping
> the gun about the severity. I just get frustrated when I see a rush to
> make changes over an incidental part of a popularized vuln, with
> disproportionate weight on "doing something" and not enough on being
> careful.

Sure, I can see it looks that way. My sense of urgency on this in
particular is that we're early in the development cycle, and it's an
ABI-breaking change, so I want to maximize how much time it has to get
tested out in real workloads. (i.e. we've now seen that just rejecting
NULL argv is likely too painful, etc.)

All that said, I regularly bemoan the lack of sufficient regression
tests for execve() and the binfmt_*.c loaders. I've written a couple,
and so have others, but what I really want is a library of "binary that
got broken by exec change" for doing regression testing against. That
gets hampered by both size, redistribution issues, etc, so I've wanted
to have minimal reproducers for each, but creating those take time, etc,
etc.

(And you'll note I wrote[1] a test for this particular behavior, because
I'm trying to avoid falling further behind in test coverage.)

I would _love_ it if someone had the time and attention to go through
all the past binaries and make a regression test series. :)

-Kees

[1] https://lore.kernel.org/linux-hardening/20220201011637.2457646-1-keescook@chromium.org/

-- 
Kees Cook

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

* Re: [PATCH] exec: Force single empty string when argv is empty
  2022-02-01  9:17 ` David Laight
@ 2022-02-02 20:31   ` Kees Cook
  0 siblings, 0 replies; 10+ messages in thread
From: Kees Cook @ 2022-02-02 20:31 UTC (permalink / raw)
  To: David Laight
  Cc: Andrew Morton, Ariadne Conill, Michael Kerrisk, Matthew Wilcox,
	Christian Brauner, Rich Felker, Eric Biederman, Alexander Viro,
	linux-fsdevel, stable, linux-kernel, linux-hardening

On Tue, Feb 01, 2022 at 09:17:47AM +0000, David Laight wrote:
> From: Kees Cook
> > Sent: 01 February 2022 00:10
> ...
> > While the initial code searches[6][7] turned up what appeared to be
> > mostly corner case tests, trying to that just reject argv == NULL
> > (or an immediately terminated pointer list) quickly started tripping[8]
> > existing userspace programs.
> > 
> > The next best approach is forcing a single empty string into argv and
> > adjusting argc to match. The number of programs depending on argc == 0
> > seems a smaller set than those calling execve with a NULL argv.
> 
> Has anyone considered using the pathname for argv[0]?
> So converting:
> 	execl(path, NULL);
> into:
> 	execl(path, path, NULL);
> 
> I've not spotted any such suggestion.

It came up on some IRC discussions at some point. I'm personally not a
fan of this because it creates a bit of "new" ABI that has a lot of
variability (depending on "" is one thing, but depending on a "missing"
argv matching the exec path is very different). I think there were also
concerns about dealing with fd-based exec ("what is the 'right' name"),
etc.

I'd prefer we stay as simple as possible for this change.

-- 
Kees Cook

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

end of thread, other threads:[~2022-02-02 20:31 UTC | newest]

Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-02-01  0:09 [PATCH] exec: Force single empty string when argv is empty Kees Cook
2022-02-01  1:00 ` Ariadne Conill
2022-02-01  2:00 ` Andy Lutomirski
2022-02-01  9:17 ` David Laight
2022-02-02 20:31   ` Kees Cook
2022-02-01 13:22 ` Christian Brauner
2022-02-01 14:53 ` Rich Felker
2022-02-02 15:50   ` Kees Cook
2022-02-02 17:12     ` Rich Felker
2022-02-02 19:03       ` 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).