(root)/
glibc-2.38/
elf/
tst-execstack.c
       1  /* Test program for making nonexecutable stacks executable
       2     on load of a DSO that requires executable stacks.  */
       3  
       4  #include <dlfcn.h>
       5  #include <stdbool.h>
       6  #include <stdio.h>
       7  #include <string.h>
       8  #include <unistd.h>
       9  #include <error.h>
      10  #include <stackinfo.h>
      11  
      12  static void
      13  print_maps (void)
      14  {
      15  #if 0
      16    char *cmd = NULL;
      17    asprintf (&cmd, "cat /proc/%d/maps", getpid ());
      18    system (cmd);
      19    free (cmd);
      20  #endif
      21  }
      22  
      23  static void deeper (void (*f) (void));
      24  
      25  #if USE_PTHREADS
      26  # include <pthread.h>
      27  
      28  static void *
      29  tryme_thread (void *f)
      30  {
      31    (*((void (*) (void)) f)) ();
      32  
      33    return 0;
      34  }
      35  
      36  static pthread_barrier_t startup_barrier, go_barrier;
      37  static void *
      38  waiter_thread (void *arg)
      39  {
      40    void **f = arg;
      41    pthread_barrier_wait (&startup_barrier);
      42    pthread_barrier_wait (&go_barrier);
      43  
      44    (*((void (*) (void)) *f)) ();
      45  
      46    return 0;
      47  }
      48  #endif
      49  
      50  static bool allow_execstack = true;
      51  
      52  
      53  static int
      54  do_test (void)
      55  {
      56    /* Check whether SELinux is enabled and disallows executable stacks.  */
      57    FILE *fp = fopen ("/selinux/enforce", "r");
      58    if (fp != NULL)
      59      {
      60        char *line = NULL;
      61        size_t linelen = 0;
      62  
      63        bool enabled = false;
      64        ssize_t n = getline (&line, &linelen, fp);
      65        if (n > 0 && line[0] != '0')
      66  	enabled = true;
      67  
      68        fclose (fp);
      69  
      70        if (enabled)
      71  	{
      72  	  fp = fopen ("/selinux/booleans/allow_execstack", "r");
      73  	  if (fp != NULL)
      74  	    {
      75  	      n = getline (&line, &linelen, fp);
      76  	      if (n > 0 && line[0] == '0')
      77  		allow_execstack = false;
      78  	    }
      79  
      80  	  fclose (fp);
      81  	}
      82      }
      83  
      84    printf ("executable stacks %sallowed\n", allow_execstack ? "" : "not ");
      85  
      86    static void *f;		/* Address of this is used in other threads. */
      87  
      88  #if USE_PTHREADS
      89    /* Create some threads while stacks are nonexecutable.  */
      90    #define N 5
      91    pthread_t thr[N];
      92  
      93    pthread_barrier_init (&startup_barrier, NULL, N + 1);
      94    pthread_barrier_init (&go_barrier, NULL, N + 1);
      95  
      96    for (int i = 0; i < N; ++i)
      97      {
      98        int rc = pthread_create (&thr[i], NULL, &waiter_thread, &f);
      99        if (rc)
     100  	error (1, rc, "pthread_create");
     101      }
     102  
     103    /* Make sure they are all there using their stacks.  */
     104    pthread_barrier_wait (&startup_barrier);
     105    puts ("threads waiting");
     106  #endif
     107  
     108    print_maps ();
     109  
     110  #if USE_PTHREADS
     111    void *old_stack_addr, *new_stack_addr;
     112    size_t stack_size;
     113    pthread_t me = pthread_self ();
     114    pthread_attr_t attr;
     115    int ret = 0;
     116  
     117    ret = pthread_getattr_np (me, &attr);
     118    if (ret)
     119      {
     120        printf ("before execstack: pthread_getattr_np returned error: %s\n",
     121  	      strerror (ret));
     122        return 1;
     123      }
     124  
     125    ret = pthread_attr_getstack (&attr, &old_stack_addr, &stack_size);
     126    if (ret)
     127      {
     128        printf ("before execstack: pthread_attr_getstack returned error: %s\n",
     129  	      strerror (ret));
     130        return 1;
     131      }
     132  # if _STACK_GROWS_DOWN
     133      old_stack_addr += stack_size;
     134  # else
     135      old_stack_addr -= stack_size;
     136  # endif
     137  #endif
     138  
     139    /* Loading this module should force stacks to become executable.  */
     140    void *h = dlopen ("tst-execstack-mod.so", RTLD_LAZY);
     141    if (h == NULL)
     142      {
     143        printf ("cannot load: %s\n", dlerror ());
     144        return allow_execstack;
     145      }
     146  
     147    f = dlsym (h, "tryme");
     148    if (f == NULL)
     149      {
     150        printf ("symbol not found: %s\n", dlerror ());
     151        return 1;
     152      }
     153  
     154    /* Test if that really made our stack executable.
     155       The `tryme' function should crash if not.  */
     156  
     157    (*((void (*) (void)) f)) ();
     158  
     159    print_maps ();
     160  
     161  #if USE_PTHREADS
     162    ret = pthread_getattr_np (me, &attr);
     163    if (ret)
     164      {
     165        printf ("after execstack: pthread_getattr_np returned error: %s\n",
     166  	      strerror (ret));
     167        return 1;
     168      }
     169  
     170    ret = pthread_attr_getstack (&attr, &new_stack_addr, &stack_size);
     171    if (ret)
     172      {
     173        printf ("after execstack: pthread_attr_getstack returned error: %s\n",
     174  	      strerror (ret));
     175        return 1;
     176      }
     177  
     178  # if _STACK_GROWS_DOWN
     179      new_stack_addr += stack_size;
     180  # else
     181      new_stack_addr -= stack_size;
     182  # endif
     183  
     184    /* It is possible that the dlopen'd module may have been mmapped just below
     185       the stack.  The stack size is taken as MIN(stack rlimit size, end of last
     186       vma) in pthread_getattr_np.  If rlimit is set high enough, it is possible
     187       that the size may have changed.  A subsequent call to
     188       pthread_attr_getstack returns the size and (bottom - size) as the
     189       stacksize and stackaddr respectively.  If the size changes due to the
     190       above, then both stacksize and stackaddr can change, but the stack bottom
     191       should remain the same, which is computed as stackaddr + stacksize.  */
     192    if (old_stack_addr != new_stack_addr)
     193      {
     194        printf ("Stack end changed, old: %p, new: %p\n",
     195  	      old_stack_addr, new_stack_addr);
     196        return 1;
     197      }
     198    printf ("Stack address remains the same: %p\n", old_stack_addr);
     199  #endif
     200  
     201    /* Test that growing the stack region gets new executable pages too.  */
     202    deeper ((void (*) (void)) f);
     203  
     204    print_maps ();
     205  
     206  #if USE_PTHREADS
     207    /* Test that a fresh thread now gets an executable stack.  */
     208    {
     209      pthread_t th;
     210      int rc = pthread_create (&th, NULL, &tryme_thread, f);
     211      if (rc)
     212        error (1, rc, "pthread_create");
     213    }
     214  
     215    puts ("threads go");
     216    /* The existing threads' stacks should have been changed.
     217       Let them run to test it.  */
     218    pthread_barrier_wait (&go_barrier);
     219  
     220    pthread_exit ((void *) (long int) (! allow_execstack));
     221  #endif
     222  
     223    return ! allow_execstack;
     224  }
     225  
     226  static void
     227  deeper (void (*f) (void))
     228  {
     229    char stack[1100 * 1024];
     230    explicit_bzero (stack, sizeof stack);
     231    (*f) ();
     232    memfrob (stack, sizeof stack);
     233  }
     234  
     235  
     236  #include <support/test-driver.c>