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:
Adhemerval Zanella 2016-01-29 11:43:40 -02:00 committed by Adhemerval Zanella
parent fee9eb6200
commit f83bb9b8e9
4 changed files with 80 additions and 119 deletions

View File

@ -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:

View File

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

View File

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

View File

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