mirror of
https://sourceware.org/git/glibc.git
synced 2024-11-21 12:30:06 +00:00
posix: Remove dynamic memory allocation from execl{e,p}
GLIBC execl{e,p} implementation might use malloc if the total number of arguments exceed initial assumption size (1024). This might lead to issues in two situations: 1. execl/execle is stated to be async-signal-safe by POSIX [1]. However if execl is used in a signal handler with a large argument set (that may call malloc internally) and if the resulting call fails it might lead malloc in the program in a bad state. 2. If the functions are used in a vfork/clone(VFORK) situation it also might issue malloc internal bad state. This patch fixes it by using stack allocation instead. It also fixes BZ#19534. Tested on x86_64. [1] http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html [BZ #19534] * posix/execl.c (execl): Remove dynamic memory allocation. * posix/execle.c (execle): Likewise. * posix/execlp.c (execlp): Likewise.
This commit is contained in:
parent
fee9eb6200
commit
f83bb9b8e9
@ -1,3 +1,10 @@
|
||||
2016-03-07 Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
||||
|
||||
[BZ #19534]
|
||||
* posix/execl.c (execl): Remove dynamic memory allocation.
|
||||
* posix/execle.c (execle): Likewise.
|
||||
* posix/execlp.c (execlp): Likewise.
|
||||
|
||||
2016-03-06 H.J. Lu <hongjiu.lu@intel.com>
|
||||
|
||||
* sysdeps/x86_64/multiarch/memcpy-avx512-no-vzeroupper.S:
|
||||
|
@ -16,58 +16,42 @@
|
||||
<http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stackinfo.h>
|
||||
|
||||
#include <sys/param.h>
|
||||
|
||||
/* Execute PATH with all arguments after PATH until
|
||||
a NULL pointer and environment from `environ'. */
|
||||
int
|
||||
execl (const char *path, const char *arg, ...)
|
||||
{
|
||||
#define INITIAL_ARGV_MAX 1024
|
||||
size_t argv_max = INITIAL_ARGV_MAX;
|
||||
const char *initial_argv[INITIAL_ARGV_MAX];
|
||||
const char **argv = initial_argv;
|
||||
va_list args;
|
||||
|
||||
argv[0] = arg;
|
||||
|
||||
va_start (args, arg);
|
||||
unsigned int i = 0;
|
||||
while (argv[i++] != NULL)
|
||||
ptrdiff_t argc;
|
||||
va_list ap;
|
||||
va_start (ap, arg);
|
||||
for (argc = 1; va_arg (ap, const char *); argc++)
|
||||
{
|
||||
if (i == argv_max)
|
||||
if (argc == INT_MAX)
|
||||
{
|
||||
argv_max *= 2;
|
||||
const char **nptr = realloc (argv == initial_argv ? NULL : argv,
|
||||
argv_max * sizeof (const char *));
|
||||
if (nptr == NULL)
|
||||
{
|
||||
if (argv != initial_argv)
|
||||
free (argv);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
if (argv == initial_argv)
|
||||
/* We have to copy the already filled-in data ourselves. */
|
||||
memcpy (nptr, argv, i * sizeof (const char *));
|
||||
|
||||
argv = nptr;
|
||||
va_end (ap);
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[i] = va_arg (args, const char *);
|
||||
}
|
||||
va_end (args);
|
||||
va_end (ap);
|
||||
|
||||
int ret = __execve (path, (char *const *) argv, __environ);
|
||||
if (argv != initial_argv)
|
||||
free (argv);
|
||||
/* Avoid dynamic memory allocation due two main issues:
|
||||
1. The function should be async-signal-safe and a running on a signal
|
||||
handler with a fail outcome might lead to malloc bad state.
|
||||
2. It might be used in a vfork/clone(VFORK) scenario where using
|
||||
malloc also might lead to internal bad state. */
|
||||
ptrdiff_t i;
|
||||
char *argv[argc + 1];
|
||||
va_start (ap, arg);
|
||||
argv[0] = (char *) arg;
|
||||
for (i = 1; i <= argc; i++)
|
||||
argv[i] = va_arg (ap, char *);
|
||||
va_end (ap);
|
||||
|
||||
return ret;
|
||||
return __execve (path, argv, __environ);
|
||||
}
|
||||
libc_hidden_def (execl)
|
||||
|
@ -17,57 +17,43 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stackinfo.h>
|
||||
#include <errno.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
/* Execute PATH with all arguments after PATH until a NULL pointer,
|
||||
and the argument after that for environment. */
|
||||
int
|
||||
execle (const char *path, const char *arg, ...)
|
||||
{
|
||||
#define INITIAL_ARGV_MAX 1024
|
||||
size_t argv_max = INITIAL_ARGV_MAX;
|
||||
const char *initial_argv[INITIAL_ARGV_MAX];
|
||||
const char **argv = initial_argv;
|
||||
va_list args;
|
||||
argv[0] = arg;
|
||||
|
||||
va_start (args, arg);
|
||||
unsigned int i = 0;
|
||||
while (argv[i++] != NULL)
|
||||
ptrdiff_t argc;
|
||||
va_list ap;
|
||||
va_start (ap, arg);
|
||||
for (argc = 1; va_arg (ap, const char *); argc++)
|
||||
{
|
||||
if (i == argv_max)
|
||||
if (argc == INT_MAX)
|
||||
{
|
||||
argv_max *= 2;
|
||||
const char **nptr = realloc (argv == initial_argv ? NULL : argv,
|
||||
argv_max * sizeof (const char *));
|
||||
if (nptr == NULL)
|
||||
{
|
||||
if (argv != initial_argv)
|
||||
free (argv);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
if (argv == initial_argv)
|
||||
/* We have to copy the already filled-in data ourselves. */
|
||||
memcpy (nptr, argv, i * sizeof (const char *));
|
||||
|
||||
argv = nptr;
|
||||
va_end (ap);
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[i] = va_arg (args, const char *);
|
||||
}
|
||||
va_end (ap);
|
||||
|
||||
const char *const *envp = va_arg (args, const char *const *);
|
||||
va_end (args);
|
||||
/* Avoid dynamic memory allocation due two main issues:
|
||||
1. The function should be async-signal-safe and a running on a signal
|
||||
handler with a fail outcome might lead to malloc bad state.
|
||||
2. It might be used in a vfork/clone(VFORK) scenario where using
|
||||
malloc also might lead to internal bad state. */
|
||||
ptrdiff_t i;
|
||||
char *argv[argc + 1];
|
||||
char **envp;
|
||||
va_start (ap, arg);
|
||||
argv[0] = (char *) arg;
|
||||
for (i = 1; i <= argc; i++)
|
||||
argv[i] = va_arg (ap, char *);
|
||||
envp = va_arg (ap, char **);
|
||||
va_end (ap);
|
||||
|
||||
int ret = __execve (path, (char *const *) argv, (char *const *) envp);
|
||||
if (argv != initial_argv)
|
||||
free (argv);
|
||||
|
||||
return ret;
|
||||
return __execve (path, argv, envp);
|
||||
}
|
||||
libc_hidden_def (execle)
|
||||
|
@ -17,11 +17,8 @@
|
||||
|
||||
#include <unistd.h>
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <stackinfo.h>
|
||||
#include <errno.h>
|
||||
#include <sys/param.h>
|
||||
|
||||
/* Execute FILE, searching in the `PATH' environment variable if
|
||||
it contains no slashes, with all arguments after FILE until a
|
||||
@ -29,45 +26,32 @@
|
||||
int
|
||||
execlp (const char *file, const char *arg, ...)
|
||||
{
|
||||
#define INITIAL_ARGV_MAX 1024
|
||||
size_t argv_max = INITIAL_ARGV_MAX;
|
||||
const char *initial_argv[INITIAL_ARGV_MAX];
|
||||
const char **argv = initial_argv;
|
||||
va_list args;
|
||||
|
||||
argv[0] = arg;
|
||||
|
||||
va_start (args, arg);
|
||||
unsigned int i = 0;
|
||||
while (argv[i++] != NULL)
|
||||
ptrdiff_t argc;
|
||||
va_list ap;
|
||||
va_start (ap, arg);
|
||||
for (argc = 1; va_arg (ap, const char *); argc++)
|
||||
{
|
||||
if (i == argv_max)
|
||||
if (argc == INT_MAX)
|
||||
{
|
||||
argv_max *= 2;
|
||||
const char **nptr = realloc (argv == initial_argv ? NULL : argv,
|
||||
argv_max * sizeof (const char *));
|
||||
if (nptr == NULL)
|
||||
{
|
||||
if (argv != initial_argv)
|
||||
free (argv);
|
||||
va_end (args);
|
||||
return -1;
|
||||
}
|
||||
if (argv == initial_argv)
|
||||
/* We have to copy the already filled-in data ourselves. */
|
||||
memcpy (nptr, argv, i * sizeof (const char *));
|
||||
|
||||
argv = nptr;
|
||||
va_end (ap);
|
||||
errno = E2BIG;
|
||||
return -1;
|
||||
}
|
||||
|
||||
argv[i] = va_arg (args, const char *);
|
||||
}
|
||||
va_end (args);
|
||||
va_end (ap);
|
||||
|
||||
int ret = execvp (file, (char *const *) argv);
|
||||
if (argv != initial_argv)
|
||||
free (argv);
|
||||
/* Although posix does not state execlp as an async-safe function
|
||||
it can not use malloc to allocate the arguments since it might
|
||||
be used in a vfork scenario and it may lead to malloc internal
|
||||
bad state. */
|
||||
ptrdiff_t i;
|
||||
char *argv[argc + 1];
|
||||
va_start (ap, arg);
|
||||
argv[0] = (char *) arg;
|
||||
for (i = 1; i <= argc; i++)
|
||||
argv[i] = va_arg (ap, char *);
|
||||
va_end (ap);
|
||||
|
||||
return ret;
|
||||
return __execvpe (file, argv, __environ);
|
||||
}
|
||||
libc_hidden_def (execlp)
|
||||
|
Loading…
Reference in New Issue
Block a user