Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
Adhemerval Zanella authored and Adhemerval Zanella committed Mar 7, 2016
1 parent fee9eb6 commit f83bb9b
Show file tree
Hide file tree
Showing 4 changed files with 86 additions and 125 deletions.
7 changes: 7 additions & 0 deletions ChangeLog
@@ -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:
Expand Down
68 changes: 26 additions & 42 deletions posix/execl.c
Expand Up @@ -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);

int ret = __execve (path, (char *const *) argv, __environ);
if (argv != initial_argv)
free (argv);

return ret;
va_end (ap);

/* 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 __execve (path, argv, __environ);
}
libc_hidden_def (execl)
70 changes: 28 additions & 42 deletions posix/execle.c
Expand Up @@ -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 *);
}

const char *const *envp = va_arg (args, const char *const *);
va_end (args);

int ret = __execve (path, (char *const *) argv, (char *const *) envp);
if (argv != initial_argv)
free (argv);

return ret;
va_end (ap);

/* 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);

return __execve (path, argv, envp);
}
libc_hidden_def (execle)
66 changes: 25 additions & 41 deletions posix/execlp.c
Expand Up @@ -17,57 +17,41 @@

#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
NULL pointer and environment from `environ'. */
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);

int ret = execvp (file, (char *const *) argv);
if (argv != initial_argv)
free (argv);

return ret;
va_end (ap);

/* 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 __execvpe (file, argv, __environ);
}
libc_hidden_def (execlp)

0 comments on commit f83bb9b

Please sign in to comment.