diff options
| author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2024-11-28 14:36:43 -0300 |
|---|---|---|
| committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2024-12-31 09:04:20 -0300 |
| commit | 0ca8785a28515291d4ef074b5b6cfb27434c1d2b (patch) | |
| tree | ba0b23b8b1bcef4d2717f1d605d32ef11518430c /elf/tst-execstack.c | |
| parent | ca96ea06b37c8601dcc9579dd4c8619322ab1ea1 (diff) | |
| download | glibc-0ca8785a28515291d4ef074b5b6cfb27434c1d2b.tar.xz glibc-0ca8785a28515291d4ef074b5b6cfb27434c1d2b.zip | |
elf: Do not change stack permission on dlopen/dlmopen
If some shared library loaded with dlopen/dlmopen requires an executable
stack, either implicitly because of a missing GNU_STACK ELF header
(where the ABI default flags implies in the executable bit) or explicitly
because of the executable bit from GNU_STACK; the loader will try to set
the both the main thread and all thread stacks (from the pthread cache)
as executable.
Besides the issue where any __nptl_change_stack_perm failure does not
undo the previous executable transition (meaning that if the library
fails to load, there can be thread stacks with executable stacks), this
behavior was used on a CVE [1] as a vector for RCE.
This patch changes that if a shared library requires an executable
stack, and the current stack is not executable, dlopen fails. The
change is done only for dynamically loaded modules, if the program
or any dependency requires an executable stack, the loader will still
change the main thread before program execution and any thread created
with default stack configuration.
[1] https://www.qualys.com/2023/07/19/cve-2023-38408/rce-openssh-forwarded-ssh-agent.txt
Checked on x86_64-linux-gnu and i686-linux-gnu.
Reviewed-by: Florian Weimer <fweimer@redhat.com>
Diffstat (limited to 'elf/tst-execstack.c')
| -rw-r--r-- | elf/tst-execstack.c | 62 |
1 files changed, 38 insertions, 24 deletions
diff --git a/elf/tst-execstack.c b/elf/tst-execstack.c index fae2930f70..66979547ad 100644 --- a/elf/tst-execstack.c +++ b/elf/tst-execstack.c @@ -23,16 +23,33 @@ #include <stackinfo.h> #include <stdbool.h> #include <string.h> +#include <stdlib.h> #include <support/xdlfcn.h> #include <support/xthread.h> #include <support/check.h> #include <support/xstdio.h> -static void deeper (void (*f) (void)); +/* The DEFAULT_RWX_STACK controls whether the toolchain enables an executable + stack for the testcase (which does not contain features that might require + an executable stack, such as nested function). + Some ABIs do require an executable stack, even if the toolchain supports + non-executable stack. In this cases the DEFAULT_RWX_STACK can be + overridden. */ +#ifndef DEFAULT_RWX_STACK +# define DEFAULT_RWX_STACK 0 +#else +static void +deeper (void (*f) (void)) +{ + char stack[1100 * 1024]; + explicit_bzero (stack, sizeof stack); + (*f) (); + memfrob (stack, sizeof stack); +} +#endif #if USE_PTHREADS -# include <pthread.h> - +# if DEFAULT_RWX_STACK static void * tryme_thread (void *f) { @@ -40,16 +57,21 @@ tryme_thread (void *f) return 0; } +# endif static pthread_barrier_t startup_barrier, go_barrier; static void * waiter_thread (void *arg) { - void **f = arg; xpthread_barrier_wait (&startup_barrier); xpthread_barrier_wait (&go_barrier); +# if DEFAULT_RWX_STACK + void **f = arg; (*((void (*) (void)) *f)) (); +# else + abort (); +# endif return 0; } @@ -91,7 +113,9 @@ do_test (void) printf ("executable stacks %sallowed\n", allow_execstack ? "" : "not "); +#if USE_PTHREADS || DEFAULT_RWX_STACK static void *f; /* Address of this is used in other threads. */ +#endif #if USE_PTHREADS /* Create some threads while stacks are nonexecutable. */ @@ -108,7 +132,7 @@ do_test (void) puts ("threads waiting"); #endif -#if USE_PTHREADS +#if USE_PTHREADS && DEFAULT_RWX_STACK void *old_stack_addr, *new_stack_addr; size_t stack_size; pthread_t me = pthread_self (); @@ -130,11 +154,10 @@ do_test (void) const char *soname = "tst-execstack-mod.so"; #endif void *h = dlopen (soname, RTLD_LAZY); - if (h == NULL) - { - printf ("cannot load: %s\n", dlerror ()); - return allow_execstack; - } +#if !DEFAULT_RWX_STACK + TEST_VERIFY_EXIT (h == NULL); +#else + TEST_VERIFY_EXIT (h != NULL); f = xdlsym (h, "tryme"); @@ -150,9 +173,9 @@ do_test (void) # if _STACK_GROWS_DOWN new_stack_addr += stack_size; -# else +# else new_stack_addr -= stack_size; -# endif +# 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 @@ -164,12 +187,12 @@ do_test (void) should remain the same, which is computed as stackaddr + stacksize. */ TEST_VERIFY_EXIT (old_stack_addr == new_stack_addr); printf ("Stack address remains the same: %p\n", old_stack_addr); -#endif +# endif /* Test that growing the stack region gets new executable pages too. */ deeper ((void (*) (void)) f); -#if USE_PTHREADS +# if USE_PTHREADS /* Test that a fresh thread now gets an executable stack. */ xpthread_create (NULL, &tryme_thread, f); @@ -179,19 +202,10 @@ do_test (void) xpthread_barrier_wait (&go_barrier); pthread_exit ((void *) (long int) (! allow_execstack)); +# endif #endif return ! allow_execstack; } -static void -deeper (void (*f) (void)) -{ - char stack[1100 * 1024]; - explicit_bzero (stack, sizeof stack); - (*f) (); - memfrob (stack, sizeof stack); -} - - #include <support/test-driver.c> |
