nsexec (CVE-2019-5736): avoid parsing environ

My first attempt to simplify this and make it less costly focussed on
the way constructors are called. I was under the impression that the ELF
specification mandated that arg, argv, and actually even envp need to be
passed to functions located in the .init_arry section (aka
"constructors"). Actually, the specifications is (cf. [2]):

SHT_INIT_ARRAY
This section contains an array of pointers to initialization functions,
as described in ``Initialization and Termination Functions'' in Chapter
5. Each pointer in the array is taken as a parameterless procedure with
a void return.

which means that this becomes a libc specific decision. Glibc passes
down those args, musl doesn't. So this approach can't work. However, we
can at least remove the environment parsing part based on POSIX since
[1] mandates that there should be an environ variable defined in
unistd.h which provides access to the environment. See also the relevant
Open Group specification [1].

[1]: http://pubs.opengroup.org/onlinepubs/9699919799/
[2]: http://www.sco.com/developers/gabi/latest/ch4.sheader.html#init_array

Fixes: CVE-2019-5736
Signed-off-by: Christian Brauner <christian.brauner@ubuntu.com>
This commit is contained in:
Christian Brauner 2019-02-14 15:56:26 +01:00
parent f414f497b5
commit bb7d8b1f41
No known key found for this signature in database
GPG Key ID: 8EB056D53EECB12D
1 changed files with 10 additions and 13 deletions

View File

@ -169,31 +169,25 @@ static int parse_xargs(char *data, int data_length, char ***output)
}
/*
* "Parse" out argv and envp from /proc/self/cmdline and /proc/self/environ.
* "Parse" out argv from /proc/self/cmdline.
* This is necessary because we are running in a context where we don't have a
* main() that we can just get the arguments from.
*/
static int fetchve(char ***argv, char ***envp)
static int fetchve(char ***argv)
{
char *cmdline = NULL, *environ = NULL;
size_t cmdline_size, environ_size;
char *cmdline = NULL;
size_t cmdline_size;
cmdline = read_file("/proc/self/cmdline", &cmdline_size);
if (!cmdline)
goto error;
environ = read_file("/proc/self/environ", &environ_size);
if (!environ)
goto error;
if (parse_xargs(cmdline, cmdline_size, argv) <= 0)
goto error;
if (parse_xargs(environ, environ_size, envp) <= 0)
goto error;
return 0;
error:
free(environ);
free(cmdline);
return -EINVAL;
}
@ -246,23 +240,26 @@ error:
return -EIO;
}
/* Get cheap access to the environment. */
extern char **environ;
int ensure_cloned_binary(void)
{
int execfd;
char **argv = NULL, **envp = NULL;
char **argv = NULL;
/* Check that we're not self-cloned, and if we are then bail. */
int cloned = is_self_cloned();
if (cloned > 0 || cloned == -ENOTRECOVERABLE)
return cloned;
if (fetchve(&argv, &envp) < 0)
if (fetchve(&argv) < 0)
return -EINVAL;
execfd = clone_binary();
if (execfd < 0)
return -EIO;
fexecve(execfd, argv, envp);
fexecve(execfd, argv, environ);
return -ENOEXEC;
}