From patchwork Fri Jun 14 12:50:52 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: 1947921 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=l36BEI+3; 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 4W0zkT5rzMz20Pb for ; Fri, 14 Jun 2024 22:54:25 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id DD2863882100 for ; Fri, 14 Jun 2024 12:54:23 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-io1-xd2a.google.com (mail-io1-xd2a.google.com [IPv6:2607:f8b0:4864:20::d2a]) by sourceware.org (Postfix) with ESMTPS id 6F2E13882643 for ; Fri, 14 Jun 2024 12:53:11 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 6F2E13882643 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 6F2E13882643 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::d2a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1718369595; cv=none; b=CoLx7ML4PcdXRwhY3w2UwsExVjvr2sfT1HZuoL7ZWlvc2NOAvaXkL0SDH9qgzyK2i1dnn0PbSVgNsgaK3GuZx82+AlQ+Ch2cyw7FOybnXt4PDpjK9MM9dElcje3H9WTfCsLA2RzO+8xTz28cBDzvWcaCdCl3i2qHZd6YNJ6dsYs= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1718369595; c=relaxed/simple; bh=mX1Diq0z75ZDJh3lESxc39SfFZOFvpLNIZJNfDqYus4=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=IUdkhiGVepczgVWJVLlCe+br8mMx/vzhIxfVAHtWbCpVTjuGf491aVtFCigDJ9xQLPW3fIqTjgS2Y3fTNbH0oJZ0A/5NUeFU6gcswXjA3nH1OtgPLrdAmwjJjghK/2mrKsTw5jqcB5HYAFwa0hrOqwUD/VY8lXJYxD0ZW97CL7Q= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-io1-xd2a.google.com with SMTP id ca18e2360f4ac-7e9c80adc25so82894439f.3 for ; Fri, 14 Jun 2024 05:53:11 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1718369590; x=1718974390; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=Pd85jhBjXCJY2vsR+twZHKxqOyAvfLL6N1NPUinhwTQ=; b=l36BEI+3w6AXGWa3JQ8H6DES4mTNWGTTWoZdQN29Oj25o/Zf/AnHkDXvZ2JMqa7FwE Ua1q4ud/b4LcXAZSYeFzL6GjZL3g3TuExGcvpJ6PxCT/c0kaqz2TGYUprTRhTX5xIEah gJFKtKIE2RcRywhnT1JmaRMf5u5xitpodqU4uQ9IFZLc2dIQMdgdGxsGrzquZxkE0CnV zqDUEPIgZvSYbSLdLcyyEGj5L8aAtHroccJDkvz8Y2bAqL4qSLOUHCye/VCM56pTRZ0r eq9xO5hLt8AogH08BY8X2aPZ6KzXJfT5ru53Fn5UeeJCXHXj1QFF2elppUUaLuv4iYwk D+/w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718369590; x=1718974390; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=Pd85jhBjXCJY2vsR+twZHKxqOyAvfLL6N1NPUinhwTQ=; b=FDAokU73v8nscbCObLW5l8sH8n4ft1hYUPzwBbpU4t3r+wxGKLZTo9EkCycVFEwt/T c7rXIRS/l34Jn1tkeNpdCjfRMOCK2V0ZUXtrkMcj+nGYmG7C3yvxV00tsa1q7DP1hHqO 08yOeeiWQQgruGhwdswNnWOzWGAm5V6bgOxBGsrhnNXauLJDZY3g8Fw4rvmTEgu1hvOa PhnSe7Ms/SDXLQhDTobSd5YXMeTCxBhHPZ1lg8W7UkvQTQAvRaucF5l46eLwSJ5EEg9D tEuZ2Lxbu/SAmuTDOKqdu83IYm0/UhaqeXay3wTGT3LKD2AYYXwKvVQfXX+pDn+mpCVS pr5A== X-Gm-Message-State: AOJu0Yy2sv1f5S932ngkWZ0TtjS3d0ZUJAqHCpBa5UEegHq+Qu62NXvq VMy66TU2UTrLOA7XJHNS9/m7qXNXE4jhifapPKhviKBShc0RM/XURJesWtxZyy8LfEz96J3GBw8 k X-Google-Smtp-Source: AGHT+IERVO1gU8Siq9TSju+TY7MDut+P/FGuqr2anpj+dXX6wGDrwuoOF2Zq9ysTSd7EUtgM2k2eYg== X-Received: by 2002:a05:6602:3f90:b0:7e1:8a93:48ef with SMTP id ca18e2360f4ac-7ebeb65dfb6mr304859039f.21.1718369589308; Fri, 14 Jun 2024 05:53:09 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c0:c5fb:2445:b09c:5736:f9f6]) by smtp.gmail.com with ESMTPSA id d2e1a72fcca58-705cc9890d5sm3062140b3a.92.2024.06.14.05.53.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 14 Jun 2024 05:53:08 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: "H . J . Lu" , Florian Weimer , Zack Weinberg Subject: [PATCH v2 2/2] elf: Make non-executable stack disable by default Date: Fri, 14 Jun 2024 09:50:52 -0300 Message-ID: <20240614125300.310236-3-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240614125300.310236-1-adhemerval.zanella@linaro.org> References: <20240614125300.310236-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-12.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, KAM_SHORT, 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 exists (although not widely deployed). Binutils 2.39 now warns of the need of executable stacks by default [1] and gcc nested function trampolines can now be heap based [2] (besides being a gcc-only extension). 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. Some ABIs still require executable stacks: hurd, Linux/hppa (where gcc/binutils still does not generate .note.GNU-stack as default [3]), and mips for some ABI on kernels older than 4.8. For such ABIs, the non-executable is not enforced. Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu. [1] https://www.redhat.com/en/blog/linkers-warnings-about-executable-stacks-and-segments [2] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=28d8c680aaea46137170fef2bd1c6a98301518dc [3] https://wiki.gentoo.org/wiki/Hardened/GNU_stack_quickstart --- Makeconfig | 1 + NEWS | 4 + elf/Makefile | 50 ++++++++- 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 | 20 ++++ nptl/Makefile | 11 +- sysdeps/hppa/dl-tunables.list | 11 ++ sysdeps/mach/hurd/dl-tunables.list | 11 ++ sysdeps/unix/sysv/linux/mips/Makefile | 13 +++ .../sysv/linux/mips/dl-tunables-arch.list | 12 ++ 17 files changed, 188 insertions(+), 79 deletions(-) create mode 100644 elf/tst-execstack-prog-static.c create mode 100644 sysdeps/hppa/dl-tunables.list create mode 100644 sysdeps/mach/hurd/dl-tunables.list create mode 100644 sysdeps/unix/sysv/linux/mips/dl-tunables-arch.list 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..3419a0a2e8 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 loaded + 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..17768d81f7 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 \ @@ -1845,16 +1856,53 @@ $(objpfx)unload8.out: $(objpfx)unload8mod1.so $(objpfx)unload8mod1x.so $(objpfx)tst-tls9-static.out: $(objpfx)tst-tlsmod5.so $(objpfx)tst-tlsmod6.so ifeq ($(have-z-execstack),yes) -$(objpfx)tst-execstack.out: $(objpfx)tst-execstack-mod.so +$(objpfx)tst-execstack: $(objpfx)tst-execstack-mod.so 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-program-cmd) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'error while loading shared libraries:.*executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(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-program-cmd) $< > $@ 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-program-cmd) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'executable stack is not allowed$$' $@ \ + grep -q 'error while loading shared libraries:.*executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) + +$(objpfx)tst-execstack-prog-static-default.out: $(objpfx)tst-execstack-prog-static + $(host-test-program-cmd) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q '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..bc8ef6effa 100644 --- a/manual/tunables.texi +++ b/manual/tunables.texi @@ -356,6 +356,26 @@ 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} (except +for Hurd, and hppa/mips on Linux which requires executables stacks). + +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..e16be8ff13 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 @@ -671,10 +672,18 @@ endif tst-exec4-ARGS = $(host-test-program-cmd) -$(objpfx)tst-execstack-threads.out: $(objpfx)tst-execstack-threads-mod.so +$(objpfx)tst-execstack-threads: $(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-program-cmd) $< > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'error while loading shared libraries:.*executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) tst-stackguard1-ARGS = --command "$(host-test-program-cmd) --child" tst-stackguard1-static-ARGS = --command "$(objpfx)tst-stackguard1-static --child" diff --git a/sysdeps/hppa/dl-tunables.list b/sysdeps/hppa/dl-tunables.list new file mode 100644 index 0000000000..2e53a235db --- /dev/null +++ b/sysdeps/hppa/dl-tunables.list @@ -0,0 +1,11 @@ +# HPPA defaults to executable stacks. +glibc { + rtld { + noexecstack { + type: INT_32 + minval: 0 + maxval: 1 + default: 0 + } + } +} diff --git a/sysdeps/mach/hurd/dl-tunables.list b/sysdeps/mach/hurd/dl-tunables.list new file mode 100644 index 0000000000..dc6a7aa21b --- /dev/null +++ b/sysdeps/mach/hurd/dl-tunables.list @@ -0,0 +1,11 @@ +# Hurd default to an executable stack. +glibc { + rtld { + noexecstack { + type: INT_32 + minval: 0 + maxval: 1 + default: 0 + } + } +} diff --git a/sysdeps/unix/sysv/linux/mips/Makefile b/sysdeps/unix/sysv/linux/mips/Makefile index d5725c69d8..2cb0f6268f 100644 --- a/sysdeps/unix/sysv/linux/mips/Makefile +++ b/sysdeps/unix/sysv/linux/mips/Makefile @@ -78,4 +78,17 @@ ASFLAGS-.o += -Wa,-execstack ASFLAGS-.os += -Wa,-execstack ASFLAGS-.op += -Wa,-execstack ASFLAGS-.oS += -Wa,-execstack +mips_noexecstack := 0 +else +mips_noexecstack := 1 endif + +# Change the default value of glibc.rtld.noexecstack based on mips-force-execstack +$(common-objpfx)dl-tunable-list.stmp: $(common-objpfx)dl-tunable-arch.list +$(common-objpfx)dl-tunable-arch.list: $(common-objpfx)dl-tunable-arch.stmp; @: +$(common-objpfx)dl-tunable-arch.stmp: \ + $(wildcard $(sysdirs:%=%/dl-tunables-arch.list)) + cp $^ ${@:stmp=T} + sed -i 's/mips_noexecstack/$(mips_noexecstack)/' ${@:stmp=T} + $(move-if-change) ${@:stmp=T} ${@:stmp=list} + touch $@ diff --git a/sysdeps/unix/sysv/linux/mips/dl-tunables-arch.list b/sysdeps/unix/sysv/linux/mips/dl-tunables-arch.list new file mode 100644 index 0000000000..c9656c4a15 --- /dev/null +++ b/sysdeps/unix/sysv/linux/mips/dl-tunables-arch.list @@ -0,0 +1,12 @@ +# MIPS migth require an executable stack on kernels older than 4.8. The +# 'mips_noexecstack' is set by configure depending on how glibc is configured. +glibc { + rtld { + noexecstack { + type: INT_32 + minval: 0 + maxval: 1 + default: mips_noexecstack + } + } +}