Blame elf/tst-execstack.c

Packit 6c4009
/* Test program for making nonexecutable stacks executable
Packit 6c4009
   on load of a DSO that requires executable stacks.  */
Packit 6c4009
Packit 6c4009
#include <dlfcn.h>
Packit 6c4009
#include <stdbool.h>
Packit 6c4009
#include <stdio.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <error.h>
Packit 6c4009
#include <stackinfo.h>
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
print_maps (void)
Packit 6c4009
{
Packit 6c4009
#if 0
Packit 6c4009
  char *cmd = NULL;
Packit 6c4009
  asprintf (&cmd, "cat /proc/%d/maps", getpid ());
Packit 6c4009
  system (cmd);
Packit 6c4009
  free (cmd);
Packit 6c4009
#endif
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void deeper (void (*f) (void));
Packit 6c4009
Packit 6c4009
#if USE_PTHREADS
Packit 6c4009
# include <pthread.h>
Packit 6c4009
Packit 6c4009
static void *
Packit 6c4009
tryme_thread (void *f)
Packit 6c4009
{
Packit 6c4009
  (*((void (*) (void)) f)) ();
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static pthread_barrier_t startup_barrier, go_barrier;
Packit 6c4009
static void *
Packit 6c4009
waiter_thread (void *arg)
Packit 6c4009
{
Packit 6c4009
  void **f = arg;
Packit 6c4009
  pthread_barrier_wait (&startup_barrier);
Packit 6c4009
  pthread_barrier_wait (&go_barrier);
Packit 6c4009
Packit 6c4009
  (*((void (*) (void)) *f)) ();
Packit 6c4009
Packit 6c4009
  return 0;
Packit 6c4009
}
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
static bool allow_execstack = true;
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
do_test (void)
Packit 6c4009
{
Packit 6c4009
  /* Check whether SELinux is enabled and disallows executable stacks.  */
Packit 6c4009
  FILE *fp = fopen ("/selinux/enforce", "r");
Packit 6c4009
  if (fp != NULL)
Packit 6c4009
    {
Packit 6c4009
      char *line = NULL;
Packit 6c4009
      size_t linelen = 0;
Packit 6c4009
Packit 6c4009
      bool enabled = false;
Packit 6c4009
      ssize_t n = getline (&line, &linelen, fp);
Packit 6c4009
      if (n > 0 && line[0] != '0')
Packit 6c4009
	enabled = true;
Packit 6c4009
Packit 6c4009
      fclose (fp);
Packit 6c4009
Packit 6c4009
      if (enabled)
Packit 6c4009
	{
Packit 6c4009
	  fp = fopen ("/selinux/booleans/allow_execstack", "r");
Packit 6c4009
	  if (fp != NULL)
Packit 6c4009
	    {
Packit 6c4009
	      n = getline (&line, &linelen, fp);
Packit 6c4009
	      if (n > 0 && line[0] == '0')
Packit 6c4009
		allow_execstack = false;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  fclose (fp);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  printf ("executable stacks %sallowed\n", allow_execstack ? "" : "not ");
Packit 6c4009
Packit 6c4009
  static void *f;		/* Address of this is used in other threads. */
Packit 6c4009
Packit 6c4009
#if USE_PTHREADS
Packit 6c4009
  /* Create some threads while stacks are nonexecutable.  */
Packit 6c4009
  #define N 5
Packit 6c4009
  pthread_t thr[N];
Packit 6c4009
Packit 6c4009
  pthread_barrier_init (&startup_barrier, NULL, N + 1);
Packit 6c4009
  pthread_barrier_init (&go_barrier, NULL, N + 1);
Packit 6c4009
Packit 6c4009
  for (int i = 0; i < N; ++i)
Packit 6c4009
    {
Packit 6c4009
      int rc = pthread_create (&thr[i], NULL, &waiter_thread, &f);
Packit 6c4009
      if (rc)
Packit 6c4009
	error (1, rc, "pthread_create");
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Make sure they are all there using their stacks.  */
Packit 6c4009
  pthread_barrier_wait (&startup_barrier);
Packit 6c4009
  puts ("threads waiting");
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  print_maps ();
Packit 6c4009
Packit 6c4009
#if USE_PTHREADS
Packit 6c4009
  void *old_stack_addr, *new_stack_addr;
Packit 6c4009
  size_t stack_size;
Packit 6c4009
  pthread_t me = pthread_self ();
Packit 6c4009
  pthread_attr_t attr;
Packit 6c4009
  int ret = 0;
Packit 6c4009
Packit 6c4009
  ret = pthread_getattr_np (me, &attr);
Packit 6c4009
  if (ret)
Packit 6c4009
    {
Packit 6c4009
      printf ("before execstack: pthread_getattr_np returned error: %s\n",
Packit 6c4009
	      strerror (ret));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  ret = pthread_attr_getstack (&attr, &old_stack_addr, &stack_size);
Packit 6c4009
  if (ret)
Packit 6c4009
    {
Packit 6c4009
      printf ("before execstack: pthread_attr_getstack returned error: %s\n",
Packit 6c4009
	      strerror (ret));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
# if _STACK_GROWS_DOWN
Packit 6c4009
    old_stack_addr += stack_size;
Packit 6c4009
# else
Packit 6c4009
    old_stack_addr -= stack_size;
Packit 6c4009
# endif
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Loading this module should force stacks to become executable.  */
Packit 6c4009
  void *h = dlopen ("tst-execstack-mod.so", RTLD_LAZY);
Packit 6c4009
  if (h == NULL)
Packit 6c4009
    {
Packit 6c4009
      printf ("cannot load: %s\n", dlerror ());
Packit 6c4009
      return allow_execstack;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  f = dlsym (h, "tryme");
Packit 6c4009
  if (f == NULL)
Packit 6c4009
    {
Packit 6c4009
      printf ("symbol not found: %s\n", dlerror ());
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* Test if that really made our stack executable.
Packit 6c4009
     The `tryme' function should crash if not.  */
Packit 6c4009
Packit 6c4009
  (*((void (*) (void)) f)) ();
Packit 6c4009
Packit 6c4009
  print_maps ();
Packit 6c4009
Packit 6c4009
#if USE_PTHREADS
Packit 6c4009
  ret = pthread_getattr_np (me, &attr);
Packit 6c4009
  if (ret)
Packit 6c4009
    {
Packit 6c4009
      printf ("after execstack: pthread_getattr_np returned error: %s\n",
Packit 6c4009
	      strerror (ret));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  ret = pthread_attr_getstack (&attr, &new_stack_addr, &stack_size);
Packit 6c4009
  if (ret)
Packit 6c4009
    {
Packit 6c4009
      printf ("after execstack: pthread_attr_getstack returned error: %s\n",
Packit 6c4009
	      strerror (ret));
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
# if _STACK_GROWS_DOWN
Packit 6c4009
    new_stack_addr += stack_size;
Packit 6c4009
# else
Packit 6c4009
    new_stack_addr -= stack_size;
Packit 6c4009
# endif
Packit 6c4009
Packit 6c4009
  /* It is possible that the dlopen'd module may have been mmapped just below
Packit 6c4009
     the stack.  The stack size is taken as MIN(stack rlimit size, end of last
Packit 6c4009
     vma) in pthread_getattr_np.  If rlimit is set high enough, it is possible
Packit 6c4009
     that the size may have changed.  A subsequent call to
Packit 6c4009
     pthread_attr_getstack returns the size and (bottom - size) as the
Packit 6c4009
     stacksize and stackaddr respectively.  If the size changes due to the
Packit 6c4009
     above, then both stacksize and stackaddr can change, but the stack bottom
Packit 6c4009
     should remain the same, which is computed as stackaddr + stacksize.  */
Packit 6c4009
  if (old_stack_addr != new_stack_addr)
Packit 6c4009
    {
Packit 6c4009
      printf ("Stack end changed, old: %p, new: %p\n",
Packit 6c4009
	      old_stack_addr, new_stack_addr);
Packit 6c4009
      return 1;
Packit 6c4009
    }
Packit 6c4009
  printf ("Stack address remains the same: %p\n", old_stack_addr);
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  /* Test that growing the stack region gets new executable pages too.  */
Packit 6c4009
  deeper ((void (*) (void)) f);
Packit 6c4009
Packit 6c4009
  print_maps ();
Packit 6c4009
Packit 6c4009
#if USE_PTHREADS
Packit 6c4009
  /* Test that a fresh thread now gets an executable stack.  */
Packit 6c4009
  {
Packit 6c4009
    pthread_t th;
Packit 6c4009
    int rc = pthread_create (&th, NULL, &tryme_thread, f);
Packit 6c4009
    if (rc)
Packit 6c4009
      error (1, rc, "pthread_create");
Packit 6c4009
  }
Packit 6c4009
Packit 6c4009
  puts ("threads go");
Packit 6c4009
  /* The existing threads' stacks should have been changed.
Packit 6c4009
     Let them run to test it.  */
Packit 6c4009
  pthread_barrier_wait (&go_barrier);
Packit 6c4009
Packit 6c4009
  pthread_exit ((void *) (long int) (! allow_execstack));
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  return ! allow_execstack;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static void
Packit 6c4009
deeper (void (*f) (void))
Packit 6c4009
{
Packit 6c4009
  char stack[1100 * 1024];
Packit 6c4009
  memfrob (stack, sizeof stack);
Packit 6c4009
  (*f) ();
Packit 6c4009
  memfrob (stack, sizeof stack);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
#include <support/test-driver.c>