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