diff options
Diffstat (limited to 'elf')
-rw-r--r-- | elf/Makefile | 21 | ||||
-rw-r--r-- | elf/tst-execstack-mod.c | 30 | ||||
-rw-r--r-- | elf/tst-execstack-needed.c | 36 | ||||
-rw-r--r-- | elf/tst-execstack.c | 133 |
4 files changed, 217 insertions, 3 deletions
diff --git a/elf/Makefile b/elf/Makefile index d5a1c3de34..36f205abb2 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -29,7 +29,8 @@ routines = $(dl-routines) dl-open dl-close dl-support dl-iteratephdr \ # profiled libraries. dl-routines = $(addprefix dl-,load cache lookup object reloc deps \ runtime error init fini debug misc \ - version profile conflict tls origin) + version profile conflict tls origin \ + execstack) all-dl-routines = $(dl-routines) $(sysdep-dl-routines) # But they are absent from the shared libc, because that code is in ld.so. elide-routines.os = $(all-dl-routines) dl-support enbl-secure \ @@ -80,6 +81,7 @@ distribute := rtld-Rules \ reldep9.c reldep9mod1.c reldep9mod2.c reldep9mod3.c \ tst-array1.exp tst-array2.exp tst-array4.exp \ tst-array2dep.c \ + tst-execstack-mod.c \ check-textrel.c dl-sysdep.h CFLAGS-dl-runtime.c = -fexceptions -fasynchronous-unwind-tables @@ -150,12 +152,14 @@ tests += loadtest restest1 preloadtest loadfail multiload origtest resolvfail \ neededtest3 neededtest4 unload2 lateglobal initfirst global \ restest2 next dblload dblunload reldep5 reldep6 reldep7 reldep8 \ circleload1 tst-tls3 tst-tls4 tst-tls5 tst-tls6 tst-tls7 tst-tls8 \ - tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-align + tst-tls10 tst-tls11 tst-tls12 tst-tls13 tst-tls14 tst-align \ + $(tests-execstack-$(have-z-execstack)) # reldep9 test-srcs = tst-pathopt tests-vis-yes = vismain tests-nodelete-yes = nodelete nodelete2 tests-nodlopen-yes = nodlopen nodlopen2 +tests-execstack-yes = tst-execstack tst-execstack-needed endif modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ testobj1_1 failobj constload2 constload3 unloadmod \ @@ -178,7 +182,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \ circlemod3 circlemod3a \ reldep8mod1 reldep8mod2 reldep8mod3 \ reldep9mod1 reldep9mod2 reldep9mod3 \ - tst-alignmod + tst-alignmod $(modules-execstack-$(have-z-execstack)) ifeq (yes,$(have-initfini-array)) modules-names += tst-array2dep endif @@ -186,6 +190,7 @@ modules-vis-yes = vismod1 vismod2 vismod3 modules-nodelete-yes = nodelmod1 nodelmod2 nodelmod3 nodelmod4 \ nodel2mod1 nodel2mod2 nodel2mod3 modules-nodlopen-yes = nodlopenmod nodlopenmod2 +modules-execstack-yes = tst-execstack-mod extra-objs += $(addsuffix .os,$(strip $(modules-names))) # We need this variable to be sure the test modules get the right CPPFLAGS. test-extras += $(modules-names) @@ -660,6 +665,16 @@ $(objpfx)tst-tls9-static: $(common-objpfx)dlfcn/libdl.a $(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so endif +ifeq ($(have-z-execstack),yes) +$(objpfx)tst-execstack: $(libdl) +$(objpfx)tst-execstack.out: $(objpfx)tst-execstack-mod.so +LDFLAGS-tst-execstack = -Wl,-z,noexecstack +LDFLAGS-tst-execstack-mod = -Wl,-z,execstack + +$(objpfx)tst-execstack-needed: $(objpfx)tst-execstack-mod.so +LDFLAGS-tst-execstack-needed = -Wl,-z,noexecstack +endif + $(objpfx)tst-array1.out: $(objpfx)tst-array1 $(elf-objpfx)$(rtld-installed-name) \ --library-path $(rpath-link)$(patsubst %,:%,$(sysdep-library-path)) \ diff --git a/elf/tst-execstack-mod.c b/elf/tst-execstack-mod.c new file mode 100644 index 0000000000..038e6550b5 --- /dev/null +++ b/elf/tst-execstack-mod.c @@ -0,0 +1,30 @@ +/* Test module for making nonexecutable stacks executable + on load of a DSO that requires executable stacks. */ + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> + +void callme (void (*callback) (void)); + +/* This is a function that makes use of executable stack by + using a local function trampoline. */ +void +tryme (void) +{ + bool ok = false; + void callback (void) { ok = true; } + + callme (&callback); + + if (ok) + printf ("DSO called ok (local %p, trampoline %p)\n", &ok, &callback); + else + abort (); +} + +void +callme (void (*callback) (void)) +{ + (*callback) (); +} diff --git a/elf/tst-execstack-needed.c b/elf/tst-execstack-needed.c new file mode 100644 index 0000000000..03090f7dd6 --- /dev/null +++ b/elf/tst-execstack-needed.c @@ -0,0 +1,36 @@ +/* Test program for making nonexecutable stacks executable + on DT_NEEDED load of a DSO that requires executable stacks. */ + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <error.h> + +extern void tryme (void); /* from tst-execstack-mod.so */ + +static void deeper (void (*f) (void)); + +static int +do_test (void) +{ + tryme (); + + /* Test that growing the stack region gets new executable pages too. */ + deeper (&tryme); + + return 0; +} + +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" diff --git a/elf/tst-execstack.c b/elf/tst-execstack.c new file mode 100644 index 0000000000..21eaa9bc90 --- /dev/null +++ b/elf/tst-execstack.c @@ -0,0 +1,133 @@ +/* Test program for making nonexecutable stacks executable + on load of a DSO that requires executable stacks. */ + +#include <dlfcn.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <error.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 int +do_test (void) +{ + void *f; + +#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 (); + + /* 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 1; + } + + 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 (); + + /* 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 (0); +#endif + + return 0; +} + +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" |