From patchwork Tue Jun 18 19:40:22 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: 1949456 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=nP8gDaPU; 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 4W3cbT109vz20Ws for ; Wed, 19 Jun 2024 05:42:29 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 4EA7B3884509 for ; Tue, 18 Jun 2024 19:42:27 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pj1-x102a.google.com (mail-pj1-x102a.google.com [IPv6:2607:f8b0:4864:20::102a]) by sourceware.org (Postfix) with ESMTPS id EC0333884506 for ; Tue, 18 Jun 2024 19:41:20 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org EC0333884506 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 EC0333884506 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::102a ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1718739683; cv=none; b=m/slEUfDIvip4JfIc+zlRkZ2JkSfXOYpR61pVUdiZ0XREXbo4Dw2sBcphY+ne3WzHXpDU4EFaXCU4G9zMqBNuDP+XIdxtQQrWs0gUqi4M84d01YNmNFBB65Ij+pSYZFafAkcLQRpr/flvyiQqBZvwikepVghrYvGpXI0JD05is8= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1718739683; c=relaxed/simple; bh=AFKzKZo1EDucHVht4TnxTOev7pJpitFbdS7YPH+uRKQ=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=mVHHhwB3l6V5SC2sLIfrYsOcCpLhs5C1xrX8zepN9P8ZgnEy8WN4i33U3Cqn3/1esFeX8fJyi4TF2IIXPKQzjuhdyHNO38CUuOhqqKYdAy0T11PyNr0UVild9DbXzbH5cm+6B8WBB0uvlGulvgHR/gphL+6tUSXLsg5ii5Y1Ti0= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pj1-x102a.google.com with SMTP id 98e67ed59e1d1-2c2c6277ef3so4710118a91.3 for ; Tue, 18 Jun 2024 12:41:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1718739679; x=1719344479; 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=chjxx9tjsBqV4FmM3sK2j+rIarvYoFXxbD0+MwtHycw=; b=nP8gDaPUxFayqmbO1Gj3roceA5ZxkYsbugB/yAZVlhFjVBAXyuXSbhbnU5tC+0kGTC AacrYAJEJoVL/QdRfZE/pH58Ej2amy9+FXYn1PPNHqlNHMhPm7iGHCVBznra8mHD0GCg w8ZlcH9zUbnq9uTCkyovq1GASc+PrNo5kLiKhqULWohzXOs1G1Bmhi2ZwEv+RvzOHuLH 8GeNkF/uv/mxEZ0V5m/RH8/3gZUnx0oN4Fn/rA0vJyXVX8WFeGQht1Rbm+JOzzK+N/7j YAhB8aeU2XVpvGqovfQDEgl7YmWylztrMTBUT2z3qqcGTUpD7F28gd/NrWzuxfebtuTe Y8hA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1718739679; x=1719344479; 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=chjxx9tjsBqV4FmM3sK2j+rIarvYoFXxbD0+MwtHycw=; b=lMnClhhJ8kSwguycYMRrObOXXXC+kJWwCLGJNf3qSR2o5Z00nd+2LjdN3Q+mnqrYlA MVk73z4bLp74O4ItzcyTEuq6jV5mOPvJ8ychrrf0r/t8Qd4JSuvi78q4iTf4DF4ZYICl rYCe3Xq/VxbhPkLvbCWOUB73EfWDxhuBBEHQxWXGiWpoT+b8S8S9EmDvn1atGKdD1dd5 36TQPdxIcRMGIIWGQiOid/RV82XScfSaNKa88euYJiz0eUR+ZntmHnhx1P3bLB/k8hD9 c+DnRmUf6GcoL2uPuVH+a1QPZUAYkkVeydFjUkOKGybEjaNGqof5ccdQm7HPXELSoTD/ CwqA== X-Gm-Message-State: AOJu0YxnSJTob4LRe3vtXhvwi9E4xfnfIBqW7RNLgRXNoP7dqpMzpw21 HO7ra5quoBlwwXuzuA/Z/G3kpYY+iZWC5ga097xvmgtJLPYwp+4VdXGah3x/jqIlUkLtrUhXWTP Q X-Google-Smtp-Source: AGHT+IHB0CyhM272OxLyjQZpjq3V2pSUHc0CZ3w/OgTBevQ8gB4HaF0i0w6e7ONgvhkQWQhNCV0uNw== X-Received: by 2002:a17:90a:bd8f:b0:2c7:22d6:98e with SMTP id 98e67ed59e1d1-2c7b5c82772mr716590a91.19.1718739679166; Tue, 18 Jun 2024 12:41:19 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c1:5c0d:de56:b6f3:eeae:13e7]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-2c50c4ce173sm6209093a91.23.2024.06.18.12.41.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 18 Jun 2024 12:41:18 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Cc: "H . J . Lu" , Florian Weimer , Zack Weinberg Subject: [PATCH v3 4/4] elf: Add glibc.rtld.execstack Date: Tue, 18 Jun 2024 16:40:22 -0300 Message-ID: <20240618194102.2059389-5-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240618194102.2059389-1-adhemerval.zanella@linaro.org> References: <20240618194102.2059389-1-adhemerval.zanella@linaro.org> MIME-Version: 1.0 X-Spam-Status: No, score=-12.5 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 The new tunable can be used to control whether executable stacks are allowed from either the main program or dependencies. The default is to allow executable stacks. The executable stacks default permission is checked agains the one provided by the PT_GNU_STACK from program headers (if present). The tunable also disables the stack permission change if any dependency requires an executable stack at loading time. Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu. --- NEWS | 4 ++++ elf/Makefile | 42 ++++++++++++++++++++++++++++++++++ elf/dl-load.c | 4 +++- elf/dl-support.c | 5 ++++ elf/dl-tunables.list | 6 +++++ elf/rtld.c | 4 ++++ elf/tst-rtld-list-tunables.exp | 1 + manual/tunables.texi | 19 +++++++++++++++ 8 files changed, 84 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 43ff2ee23b..a5bbf3851b 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.execstack, can be used to control whether + executable stacks are allowed from either main program or dependencies. + The default is to allow 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 604b7df1cc..dba6a6ddac 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -546,6 +546,11 @@ tests-execstack-yes = \ tests-execstack-static-yes = \ tst-execstack-prog-static # tests-execstack-static-yes +tests-execstack-special-yes = \ + $(objpfx)tst-execstack-prog-noexecstack.out \ + $(objpfx)tst-execstack-needed-noexecstack.out \ + $(objpfx)tst-execstack-prog-static-noexecstack.out \ + # tests-execstack-special-yes endif ifeq ($(have-depaudit),yes) tests += \ @@ -634,6 +639,7 @@ $(objpfx)tst-rtld-does-not-exist.out: tst-rtld-does-not-exist.sh $(objpfx)ld.so 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 \ @@ -1863,6 +1869,42 @@ CFLAGS-tst-execstack-mod.c += -Wno-trampolines LDFLAGS-tst-execstack-prog-static = -Wl,-z,execstack CFLAGS-tst-execstack-prog-static.c += -Wno-trampolines + +ifeq (yes,$(build-hardcoded-path-in-tests)) +tst-execstack-prog-noexecstack-msg = "Fatal glibc error: executable stack is not allowed$$" +else +tst-execstack-prog-noexecstack-msg = "error while loading shared libraries:.*cannot enable executable stack as shared object requires:" +endif + +$(objpfx)tst-execstack-prog-noexecstack.out: $(objpfx)tst-execstack-prog + $(test-program-cmd-before-env) \ + $(run-program-env) \ + GLIBC_TUNABLES=glibc.rtld.execstack=0 \ + $(test-program-cmd-after-env) $< \ + > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q $(tst-execstack-prog-noexecstack-msg) $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) + +$(objpfx)tst-execstack-needed-noexecstack.out: $(objpfx)tst-execstack-needed + $(test-program-cmd-before-env) \ + $(run-program-env) \ + GLIBC_TUNABLES=glibc.rtld.execstack=0 \ + $(test-program-cmd-after-env) $< \ + > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'error while loading shared libraries:.*cannot enable executable stack as shared object requires:' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) + +$(objpfx)tst-execstack-prog-static-noexecstack.out: $(objpfx)tst-execstack-prog-static + $(test-program-cmd-before-env) \ + $(run-program-env) \ + GLIBC_TUNABLES=glibc.rtld.execstack=0 \ + $< \ + > $@ 2>&1; echo "status: $$?" >> $@; \ + grep -q 'Fatal glibc error: executable stack is not allowed$$' $@ \ + && grep -q '^status: 127$$' $@; \ + $(evaluate-test) endif LDFLAGS-tst-array2 = -Wl,--no-as-needed diff --git a/elf/dl-load.c b/elf/dl-load.c index 015595aac4..75550fe089 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 @@ -1300,7 +1301,8 @@ _dl_map_object_from_fd (const char *name, const char *origname, int fd, /* The stack is presently not executable, but this module requires that it be executable. Only tries to change the stack protection during process startup. */ - if ((mode & __RTLD_DLOPEN) == 0) + if ((mode & __RTLD_DLOPEN) == 0 + && TUNABLE_GET (glibc, rtld, execstack, int32_t, NULL) == 1) #if PTHREAD_IN_LIBC errval = _dl_make_stacks_executable (stack_endp); #else diff --git a/elf/dl-support.c b/elf/dl-support.c index 451932dd03..b674468572 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, execstack, int32_t, NULL) == 0) + _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..2d6febc249 100644 --- a/elf/dl-tunables.list +++ b/elf/dl-tunables.list @@ -142,6 +142,12 @@ glibc { maxval: 1 default: 0 } + execstack { + type: INT_32 + minval: 0 + maxval: 1 + default: 1 + } } mem { diff --git a/elf/rtld.c b/elf/rtld.c index e9525ea987..5793f291e4 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, execstack, int32_t, NULL) == 0) + _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-rtld-list-tunables.exp b/elf/tst-rtld-list-tunables.exp index db0e1c86e9..9f5990f340 100644 --- a/elf/tst-rtld-list-tunables.exp +++ b/elf/tst-rtld-list-tunables.exp @@ -13,5 +13,6 @@ glibc.malloc.top_pad: 0x20000 (min: 0x0, max: 0x[f]+) 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.execstack: 1 (min: 0, max: 1) glibc.rtld.nns: 0x4 (min: 0x1, max: 0x10) glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+) diff --git a/manual/tunables.texi b/manual/tunables.texi index 8dd02d8149..f1674f31ea 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.execstack +@theglibc{} will use either the default architecture flags (that might contain +the executable bit) or the value of @code{PT_GNU_STACK} (if present), and if +the program or any shared library dependency require an executable stack the +loader will change the main stack permission if kernel starts with a non +executable stack. + +The @code{glibc.rtld.execstack} tunable allows the user to control whether +to control executable stacks from the main program or dependencies. Setting +its value to @code{0} disable executable stacks, where @code{1} enables 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. +@strong{NB:} Trying to load a dynamic shared library with @code{dlopen} or +@code{dlmopen} that requires an executable stack will always fail if the +default flags does not contain the executable bit. +@end deftp + @node Elision Tunables @section Elision Tunables @cindex elision tunables