[PATCH v2 0/3] posix: Execute file function fixes

Paul Eggert eggert@cs.ucla.edu
Fri Feb 19 23:13:00 GMT 2016


On 02/19/2016 12:19 PM, Adhemerval Zanella wrote:
> And it is a regression only if you consider dynamic allocation an alternative,

It's a regression in that it would break some applications that 
currently work. An application that is simple (no signal handling, 
single-threaded, etc.) can invoke programs with many arguments now, but 
the same application won't work if that patch is installed.

Come to think of it, execlp, posix_spawn, and posix_spawnp can all use 
malloc, since POSIX does not require them to be async-signal-safe. So 
you can go back to using malloc for their implementations, when they are 
given large argument vectors. The only conformance problem here will be 
execl and execle.

For these two, I suggest a machine-dependent implementation, with the 
default being something along the lines of the attached (this is totally 
untested, I'm just trying to give the gist).

> A better strategy would be set either hard limits on stack usage in design phase or to focus on enable GLIBC to use a better stack protection mechanism (preferable through compiler assistance).

The latter sounds like a good idea, but is a bigger project and I doubt 
whether we'd want to wait for that project to be finished before fixing 
the exec-family problems in question.
-------------- next part --------------
#include <unistd.h>
#include <stdarg.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>

int
execl (const char *file, const char *arg, ...)
{
  enum { N = 1024 };
  char *argv[N];
  va_list args;
  int argc = 0;

  argv[0] = (char *) arg;

  va_start (args, arg);
  while (argv[argc++] != NULL && argc < N)
    argv[argc] = va_arg (args, char *);
  va_end (args);

  if (argv[argc - 1] == NULL)
    return __execve (file, argv, __environ);

  /* The argument vector does not fit into ARGV.  Find out where in
     the stack the caller put the arguments, store the initial
     arguments there (as they are often cached in registers), and use
     that part of the stack as an argument vector.  If the exec fails,
     restore the temporarily-trashed stack.  This is not guaranteed to
     work on all platforms, but it should be portable to most, and the
     exceptions need to have machine-specific replacements.  */
  char **parg = (char **) &arg;
  for (int i = N / 2; ; i++)
    {
      int j;
      for (j = 0; parg[i + j] == argv[N / 2 + j]; j++)
	if (j == N / 2)
	  {
	    char **reused_argv = parg + i - N / 2;
	    size_t half_size = N / 2 * sizeof *argv;

	    /* Save the part of the stack that will be temporarily trashed.  */
	    memcpy (argv + N / 2, reused_argv, half_size);

	    /* Temporarily trash the stack.  */
	    memcpy (reused_argv, argv, half_size);

	    __execve (file, reused_argv, __environ);

	    /* Restore the temporarily-trashed stack.  */
	    memcpy (reused_argv, argv + N / 2, half_size);

	    return -1;
	  }
    }
}


More information about the Libc-alpha mailing list