From patchwork Wed Jun 12 21:09:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella Netto X-Patchwork-Id: 1947125 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=linaro.org header.i=@linaro.org header.a=rsa-sha256 header.s=google header.b=BI7oaMem; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=sourceware.org (client-ip=2620:52:3:1:0:246e:9693:128c; helo=server2.sourceware.org; envelope-from=libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [IPv6:2620:52:3:1:0:246e:9693:128c]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4Vzyqx6Nplz20Vx for ; Thu, 13 Jun 2024 07:10:37 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id E5DA83882072 for ; Wed, 12 Jun 2024 21:10:34 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pl1-x632.google.com (mail-pl1-x632.google.com [IPv6:2607:f8b0:4864:20::632]) by sourceware.org (Postfix) with ESMTPS id EA8E03882047 for ; Wed, 12 Jun 2024 21:10:07 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EA8E03882047 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=linaro.org Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=linaro.org ARC-Filter: OpenARC Filter v1.0.0 sourceware.org EA8E03882047 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::632 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1718226613; cv=none; b=CqQNHX2fv6EXF8iNTYgryMsl7KWuGSuTTjlTaRrstMokQuf53GnERgzlrsMWz0/a+tVoDSZJxZw42u3jo5nahI7t2gYonHjC6kjb2ib8bC8p3MSNN3fbfgZuI8vmzjs8ygHPDKvtXK0cxhmonPbGfCAdqQmJ6Jj+6wWIRIzsW0s= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1718226613; c=relaxed/simple; bh=GMxqBfizS5I8Ztn/xVpErF+cDpeKyu07bRrwKbwEXNE=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=DkrggiUqPX0vvCwFvYYODw8h/s93Y0sNq7UEzMDCAUXXZNv4ULl7B1RzpQByS/JsG1KfgQe0NOxE2nHkSwo8d7clba9j2xhIHRlJhxGhW1yRHSHlPtSCIMRpi1hlhqE3XhtTQ5cJX2l4Ki44LtI5t9sZPL3/I+7ZyQurYkaK/vY= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pl1-x632.google.com with SMTP id d9443c01a7336-1f7274a453bso3145315ad.2 for ; Wed, 12 Jun 2024 14:10:07 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1718226605; x=1718831405; darn=sourceware.org; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:from:to:cc:subject:date:message-id:reply-to; bh=DMUUNYHKHd0KL98rG0hvnbgmCqaYljwWKc5vBfsKISg=; b=BI7oaMem+OGGlJ3LZPQA1eEDeDjYWAx3aE6Qag67Y2CkoIxXRGI3/DtwDmbA04kn0D yoZK3jsec5yDTjGkjFOMf7P3xpSeJXkw9dTbnNsdmHyYH7mOecL6FE9Sn9RcPXBPe5X8 aB2rQCcrVDORZSXqJMAdft6M32BtkgG4ouUw6CNF1lxoaKn15Yok8tD+2GePnMyc1ljz bznLEhslnnWYKfMcIwGNCsiuqBwZj+7nIBU+qBhlP2CBzFJ7Y81n4OirFXXSGY5ZNwVs SuC0G/QBYuJFO/0yREwlLX+n/y1eZt0s8zn/h65UAzE6hGBxwAlpPRac9SjhPNu0R4Et 3eBw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718226605; x=1718831405; h=content-transfer-encoding:mime-version:message-id:date:subject:to :from:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=DMUUNYHKHd0KL98rG0hvnbgmCqaYljwWKc5vBfsKISg=; b=RG06vSJBv9ohIZfN8FO7MjVRAZn0hqtf5TGJ9XnkkpxNBeUlTBFC9O9dU53CkKuOF2 whpi0CJzPDX794mobfUXzC9s5GFPHq8ThpIzuciRP6K88jK7On53IfCiozz1xy/L4uls oC9ngKJTYwQMgVSzScSVwnXh5TEa5jecGpu6DzbLj0ADtTEPQfXsDs3P43w30DwCWstv wThYyghCepPlDU/Dza9Ojk3G25Ok5wGvFTz5HMQnySbBYYFfXx+1mpg1i56a/F2l80gI 1EfH/kUD7MlYILjiqt8eh1WmkJ2GMJj3SnYaZ7riqPWxu+xHTCSxFbip9R0fanna/8f6 LgEw== X-Gm-Message-State: AOJu0YxEht15qHLTzmhWAIh2zcsehPcx1cjIfxtHw9StZNnpXDNjoSJc 3rejKFGPmK23n3sSEuuL8JCTBu+1LEBcAeazogk7IpqdnFR+We4Drc6KgSNcGAn+CQXf/33N92+ f X-Google-Smtp-Source: AGHT+IFqlibsFjYjQSl3usYlH6G9vjIL9uxxcQc1yZX2C3RU4mHnPGe1XesjWDL7QTSzGKhvxvDKiA== X-Received: by 2002:a17:903:230f:b0:1f6:daa6:e774 with SMTP id d9443c01a7336-1f83b70b63fmr38703705ad.61.1718226604889; Wed, 12 Jun 2024 14:10:04 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c0:c5fb:12d3:2fd6:fb03:b623]) by smtp.gmail.com with ESMTPSA id d9443c01a7336-1f72110e4b5sm52365315ad.125.2024.06.12.14.10.03 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 12 Jun 2024 14:10:04 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Subject: [PATCH] elf: Make non-executable stack disable by default Date: Wed, 12 Jun 2024 18:09:38 -0300 Message-ID: <20240612211000.987116-1-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 MIME-Version: 1.0 X-Spam-Status: No, score=-12.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: libc-alpha@sourceware.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Libc-alpha mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: libc-alpha-bounces+incoming=patchwork.ozlabs.org@sourceware.org It is past time to not allow executable stacks as default, even if kernel protection like SELinux already exist (although not widely deployed). For compatibility, a new tunable, glibc.rtld.noexecstack, can be use to allow programs to run with an executable stack. It has the side effect of complete disable executable stacks for setuid binaries, but allowing in first place is *really* a bad idea. Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu. --- Makeconfig | 1 + NEWS | 4 ++ elf/Makefile | 45 ++++++++++++++ elf/dl-load.c | 8 +++ elf/dl-support.c | 5 ++ elf/dl-tunables.list | 6 ++ elf/rtld.c | 4 ++ elf/tst-execstack-prog-static.c | 1 + elf/tst-execstack.c | 106 +++++++++----------------------- elf/tst-ro-dynamic-mod.map | 3 +- elf/tst-rtld-list-tunables.exp | 1 + manual/tunables.texi | 19 ++++++ nptl/Makefile | 7 +++ 13 files changed, 133 insertions(+), 77 deletions(-) create mode 100644 elf/tst-execstack-prog-static.c diff --git a/Makeconfig b/Makeconfig index 29819363da..76f5772272 100644 --- a/Makeconfig +++ b/Makeconfig @@ -356,6 +356,7 @@ endif # Must be supported by the linker. no-whole-archive = -Wl,--no-whole-archive whole-archive = -Wl,--whole-archive +noexecstack = -Wl,-z,noexecstack # Installed name of the startup code. # The ELF convention is that the startfile is called crt1.o diff --git a/NEWS b/NEWS index 20e263f581..be589cb616 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,10 @@ Major new features: * On Linux, update epoll header to include epoll ioctl definitions and related structure added in Linux kernel 6.9. +* A new tunable, glibc.rtld.noexecstack, can be used to enable executable + stacks from either main program, dependencies, or dynamically loadeed + libraries. The default is to disable executable stacks. + Deprecated and removed features, and other changes affecting compatibility: * Architectures which use a 32-bit seconds-since-epoch field in struct diff --git a/elf/Makefile b/elf/Makefile index 57b3a19d36..56414fcbba 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -543,6 +543,15 @@ tests-execstack-yes = \ tst-execstack-needed \ tst-execstack-prog \ # tests-execstack-yes +tests-execstack-static-yes = \ + tst-execstack-prog-static \ + # tests-execstack-static-yes +tests-execstack-special-yes = \ + $(objpfx)tst-execstack-default.out \ + $(objpfx)tst-execstack-needed-default.out \ + $(objpfx)tst-execstack-prog-default.out \ + $(objpfx)tst-execstack-prog-static-default.out \ + # tests-execstack-special-yes endif ifeq ($(have-depaudit),yes) tests += \ @@ -630,6 +639,8 @@ $(objpfx)tst-rtld-does-not-exist.out: tst-rtld-does-not-exist.sh $(objpfx)ld.so $(evaluate-test) tests += $(tests-execstack-$(have-z-execstack)) +tests-static += $(tests-execstack-static-$(have-z-execstack)) +tests-special += $(tests-execstack-special-$(have-z-execstack)) ifeq ($(run-built-tests),yes) tests-special += \ $(objpfx)noload-mem.out \ @@ -1850,11 +1861,45 @@ CPPFLAGS-tst-execstack.c += -DUSE_PTHREADS=0 LDFLAGS-tst-execstack = -Wl,-z,noexecstack LDFLAGS-tst-execstack-mod.so = -Wl,-z,execstack +$(objpfx)tst-execstack-default.out: $(objpfx)tst-execstack + $(test-wrapper-env) $(run-program-env) $< > $@; \ + $(evaluate-test) + +tst-execstack-ENV = GLIBC_TUNABLES=glibc.rtld.noexecstack=0 \ + ALLOW_EXECSTACK=1 + $(objpfx)tst-execstack-needed: $(objpfx)tst-execstack-mod.so LDFLAGS-tst-execstack-needed = -Wl,-z,noexecstack +tst-execstack-needed-ENV = GLIBC_TUNABLES=glibc.rtld.noexecstack=0 \ + ALLOW_EXECSTACK=1 + +$(objpfx)tst-execstack-needed-default.out: $(objpfx)tst-execstack-needed + $(test-wrapper-env) $(run-program-env) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'error while loading shared libraries:.*executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) + LDFLAGS-tst-execstack-prog = -Wl,-z,execstack CFLAGS-tst-execstack-prog.c += -Wno-trampolines +tst-execstack-prog-ENV = GLIBC_TUNABLES=glibc.rtld.noexecstack=0 + +LDFLAGS-tst-execstack-prog-static = -Wl,-z,execstack +CFLAGS-tst-execstack-prog-static.c += -Wno-trampolines +tst-execstack-prog-static-ENV = GLIBC_TUNABLES=glibc.rtld.noexecstack=0 + +$(objpfx)tst-execstack-prog-default.out: $(objpfx)tst-execstack-prog + $(test-wrapper-env) $(run-program-env) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'Fatal glibc error: executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) + +$(objpfx)tst-execstack-prog-static-default.out: $(objpfx)tst-execstack-prog-static + $(test-wrapper-env) $(run-program-env) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'Fatal glibc error: executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) + CFLAGS-tst-execstack-mod.c += -Wno-trampolines endif diff --git a/elf/dl-load.c b/elf/dl-load.c index 8a89b71016..414955694f 100644 --- a/elf/dl-load.c +++ b/elf/dl-load.c @@ -32,6 +32,7 @@ #include #include #include +#include /* Type for the buffer we put the ELF header and hopefully the program header. This buffer does not really have to be too large. In most @@ -1297,6 +1298,13 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, if (__glibc_unlikely ((stack_flags &~ GL(dl_stack_flags)) & PF_X)) { + if (TUNABLE_GET (glibc, rtld, noexecstack, int32_t, NULL) == 1) + { + errstring = N_("\ +executable stack is not allowed"); + goto lose; + } + /* The stack is presently not executable, but this module requires that it be executable. */ #if PTHREAD_IN_LIBC diff --git a/elf/dl-support.c b/elf/dl-support.c index 451932dd03..084e2579b1 100644 --- a/elf/dl-support.c +++ b/elf/dl-support.c @@ -45,6 +45,7 @@ #include #include #include +#include extern char *__progname; char **_dl_argv = &__progname; /* This is checked for some error messages. */ @@ -335,6 +336,10 @@ _dl_non_dynamic_init (void) break; } + if ((__glibc_unlikely (GL(dl_stack_flags)) & PF_X) + && TUNABLE_GET (glibc, rtld, noexecstack, int32_t, NULL) == 1) + _dl_fatal_printf ("Fatal glibc error: executable stack is not allowed\n"); + call_function_static_weak (_dl_find_object_init); /* Setup relro on the binary itself. */ diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list index 1186272c81..745c9ce2c1 100644 --- a/elf/dl-tunables.list +++ b/elf/dl-tunables.list @@ -142,6 +142,12 @@ glibc { maxval: 1 default: 0 } + noexecstack { + type: INT_32 + minval: 0 + maxval: 1 + default: 1 + } } mem { diff --git a/elf/rtld.c b/elf/rtld.c index e9525ea987..0762d68d68 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1668,6 +1668,10 @@ dl_main (const ElfW(Phdr) *phdr, bool has_interp = rtld_setup_main_map (main_map); + if ((__glibc_unlikely (GL(dl_stack_flags)) & PF_X) + && TUNABLE_GET (glibc, rtld, noexecstack, int32_t, NULL) == 1) + _dl_fatal_printf ("Fatal glibc error: executable stack is not allowed\n"); + /* If the current libname is different from the SONAME, add the latter as well. */ if (GL(dl_rtld_map).l_info[DT_SONAME] != NULL diff --git a/elf/tst-execstack-prog-static.c b/elf/tst-execstack-prog-static.c new file mode 100644 index 0000000000..180657e5ef --- /dev/null +++ b/elf/tst-execstack-prog-static.c @@ -0,0 +1 @@ +#include "tst-execstack-prog.c" diff --git a/elf/tst-execstack.c b/elf/tst-execstack.c index 560b353918..56fb8bd874 100644 --- a/elf/tst-execstack.c +++ b/elf/tst-execstack.c @@ -8,17 +8,10 @@ #include #include #include - -static void -print_maps (void) -{ -#if 0 - char *cmd = NULL; - asprintf (&cmd, "cat /proc/%d/maps", getpid ()); - system (cmd); - free (cmd); -#endif -} +#include +#include +#include +#include static void deeper (void (*f) (void)); @@ -47,7 +40,7 @@ waiter_thread (void *arg) } #endif -static bool allow_execstack = true; +static bool kernel_allow_execstack = true; static int @@ -74,61 +67,46 @@ do_test (void) { n = getline (&line, &linelen, fp); if (n > 0 && line[0] == '0') - allow_execstack = false; + kernel_allow_execstack = false; } fclose (fp); } } - printf ("executable stacks %sallowed\n", allow_execstack ? "" : "not "); + printf ("kernel allows executable stacks: %s\n", + kernel_allow_execstack ? "yes" : "not "); + + bool glibc_allow_execstack = getenv ("ALLOW_EXECSTACK") != 0; + printf ("expected allow executable stacks: %s\n", + glibc_allow_execstack ? "yes" : "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); + xpthread_barrier_init (&startup_barrier, NULL, N + 1); + xpthread_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"); - } + xpthread_create (NULL, &waiter_thread, &f); /* Make sure they are all there using their stacks. */ - pthread_barrier_wait (&startup_barrier); + xpthread_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; - } + TEST_VERIFY_EXIT (pthread_getattr_np (me, &attr) == 0); + TEST_VERIFY_EXIT (pthread_attr_getstack (&attr, &old_stack_addr, + &stack_size) == 0); # if _STACK_GROWS_DOWN old_stack_addr += stack_size; # else @@ -146,39 +124,22 @@ do_test (void) if (h == NULL) { printf ("cannot load: %s\n", dlerror ()); - return allow_execstack; + return kernel_allow_execstack + ? (glibc_allow_execstack ? 1 : 0) + : 0; } - f = dlsym (h, "tryme"); - if (f == NULL) - { - printf ("symbol not found: %s\n", dlerror ()); - return 1; - } + f = xdlsym (h, "tryme"); /* 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; - } + TEST_VERIFY_EXIT (pthread_getattr_np (me, &attr) == 0); + TEST_VERIFY_EXIT (pthread_attr_getstack (&attr, &new_stack_addr, + &stack_size) == 0); # if _STACK_GROWS_DOWN new_stack_addr += stack_size; @@ -206,26 +167,19 @@ do_test (void) /* 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"); - } + xpthread_create (NULL, &tryme_thread, f); puts ("threads go"); /* The existing threads' stacks should have been changed. Let them run to test it. */ - pthread_barrier_wait (&go_barrier); + xpthread_barrier_wait (&go_barrier); - pthread_exit ((void *) (long int) (! allow_execstack)); + pthread_exit ((void *) (long int) (! kernel_allow_execstack)); #endif - return ! allow_execstack; + return ! kernel_allow_execstack; } static void diff --git a/elf/tst-ro-dynamic-mod.map b/elf/tst-ro-dynamic-mod.map index 2fe4a2998c..b68b036d3a 100644 --- a/elf/tst-ro-dynamic-mod.map +++ b/elf/tst-ro-dynamic-mod.map @@ -4,7 +4,7 @@ SECTIONS .dynamic : { *(.dynamic) } :text :dynamic .rodata : { *(.data*) *(.bss*) } :text /DISCARD/ : { - *(.note.gnu.property) + *(.note.gnu.property) *(.note.GNU-stack) } .note : { *(.note.*) } :text :note } @@ -13,4 +13,5 @@ PHDRS text PT_LOAD FLAGS(5) FILEHDR PHDRS; dynamic PT_DYNAMIC FLAGS(4); note PT_NOTE FLAGS(4); + gnu_stack PT_GNU_STACK FLAGS(6); } diff --git a/elf/tst-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp index db0e1c86e9..2ac06e083a 100644 --- a/elf/tst-rtld-list-tunables.exp +++ b/elf/tst-rtld-list-tunables.exp @@ -14,4 +14,5 @@ glibc.malloc.trim_threshold: 0x0 (min: 0x0, max: 0x[f]+) glibc.rtld.dynamic_sort: 2 (min: 1, max: 2) glibc.rtld.enable_secure: 0 (min: 0, max: 1) glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10) +glibc.rtld.noexecstack: 1 (min: 0, max: 1) glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+) diff --git a/manual/tunables.texi b/manual/tunables.texi index 8dd02d8149..6fcf0fb451 100644 --- a/manual/tunables.texi +++ b/manual/tunables.texi @@ -356,6 +356,25 @@ tests for @code{AT_SECURE} programs and not meant to be a security feature. The default value of this tunable is @samp{0}. @end deftp +@deftp Tunable glibc.rtld.noexecstack +Initially, @theglibc{} will use either the default architecture flags (that might +contain the executable bit) or the value of @code{PT_GNU_STACK} (if present). +If any shared library dependency or dynamic object loaded with @code{dlopen} +or @code{dlmopen} requires an executable stack (either by the default flags +or @code{PT_GNU_STACK} from the library) @theglibc{} will try to change the +permission of the stack to enable execution for all running threads. + +The @code{glibc.rtld.noexecstack} tunable allows the user to control whether +to control executable stacks from the main program, dependencies, or from +dynamically loaded libraries. Setting its value to @code{0} allows executable +stacks, where @code{1} disables it. The default value is @code{1}. + +When executable stacks are not allowed, and if the main program or dependencies +require an executable stack, the loader will fail with an error message. Trying +to load a dynamic shared library with @code{dlopen} or @code{dlmopen} will fail, +with a proper message that can be obtained with @code{dlerror}. +@end deftp + @node Elision Tunables @section Elision Tunables @cindex elision tunables diff --git a/nptl/Makefile b/nptl/Makefile index b3f8af2e1c..e504d43e22 100644 --- a/nptl/Makefile +++ b/nptl/Makefile @@ -468,6 +468,7 @@ tests-internal += \ # tests-internal ifeq ($(have-z-execstack),yes) tests += tst-execstack-threads +tests-special += $(objpfx)tst-execstack-threads-default.out endif endif @@ -675,6 +676,12 @@ $(objpfx)tst-execstack-threads.out: $(objpfx)tst-execstack-threads-mod.so LDFLAGS-tst-execstack-threads = -Wl,-z,noexecstack LDFLAGS-tst-execstack-threads-mod.so = -Wl,-z,execstack CFLAGS-tst-execstack-threads-mod.c += -Wno-trampolines +tst-execstack-threads-ENV = GLIBC_TUNABLES=glibc.rtld.noexecstack=0 \ + ALLOW_EXECSTACK=1 + +$(objpfx)tst-execstack-threads-default.out: $(objpfx)tst-execstack-threads + $(test-wrapper-env) $(run-program-env) $< > $@; \ + $(evaluate-test) tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child" tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child"