From patchwork Thu Jul 25 20:05:20 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Adhemerval Zanella X-Patchwork-Id: 1964858 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=unX0iR/5; 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 4WVMWn19Q1z1ydm for ; Fri, 26 Jul 2024 06:13:09 +1000 (AEST) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 54BEB3858288 for ; Thu, 25 Jul 2024 20:13:07 +0000 (GMT) X-Original-To: libc-alpha@sourceware.org Delivered-To: libc-alpha@sourceware.org Received: from mail-pg1-x52f.google.com (mail-pg1-x52f.google.com [IPv6:2607:f8b0:4864:20::52f]) by sourceware.org (Postfix) with ESMTPS id DE084385840C for ; Thu, 25 Jul 2024 20:12:18 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org DE084385840C 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 DE084385840C Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::52f ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721938341; cv=none; b=rKRt5kPziU5I5FUTdrsl4J60y0NZ1xdXn1A3L6RvRTQREy39qM2Jg4ogMxyufPE5OQ4FCQRZQfAgz0T8gsizDadJrFlPGKVmGxLi+J7mr8jUDk5c/I+NaNnpp01ibvTVJXRwPLtoYcGLHLQjnDhScMRUMycfofvEqcfQ0kIDe9g= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1721938341; c=relaxed/simple; bh=MFNnW/PsOgJ7cxHvHLkSw4WN6T/WMm3nh3AJZvzI6p8=; h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version; b=T+T+IuxL9c7GeVbpsS1sGhK/ws74dFdWoIPOUDfegDGx0R0E3edSFerKyw0zRJ0oi5wMMr7Sg4AxcqRouJq4rCFBON9wMq9dmpmW3xsM0Ht7L+DdvM+Ulik++xeb7iqM5oqRaJHDN3BS7yAIP7gpOWj5W608QHLrC9XOYtujoe8= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-pg1-x52f.google.com with SMTP id 41be03b00d2f7-7a23fbb372dso217862a12.0 for ; Thu, 25 Jul 2024 13:12:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=linaro.org; s=google; t=1721938337; x=1722543137; darn=sourceware.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:from:to:cc:subject:date:message-id :reply-to; bh=hefeNDn0koMuZTiL4TZ4Geh7ShTUxzw5UguObTrjgl4=; b=unX0iR/5946bRL3ogFPFAZ8poR6Bbx1C6C8fGpmMOAIcE+NZdP/7uDKTCanis/QqKQ qn5RdBSmVWczxvH1j7u12I/wVB1LdT8Y7omr6THCpCKZOR5PS0Z263Pu3oABX9jFKsZX PKOV3EKXEDOqDIgkV8PACwTYBDa9B59zhUpB7N3ZgFDLZMioQXFdm7kz0e8wyF8/WVPm NGeu8Z2QrLNXqLlHSasDo882mtEtZlt+DIQ3NuEa6QdrovRJbOpJ5kEd/qH508oo7Gyi i3gmsNaVzgmKFI68Rn5hywYp+ghgv6ILEYDJi5OJp4YlwZ+sDFMx8pzcjJpUcq1ntCWo liHA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1721938337; x=1722543137; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=hefeNDn0koMuZTiL4TZ4Geh7ShTUxzw5UguObTrjgl4=; b=vsSfB91EK6YgoN+hESywNAFKFaMYVT9Ll/h3e1XNVOt+qeU7bOdeFnr/jT2ckaVqmh swHt/2utJ4JexYYZc2kmCoTJgCIcjotM3nCsxZjqi/2DYH/5GfLbeA63HrFj8mZ4oPhR QI2v6mEFwdFgsw2TY1nzsMjibwhL1iQnj1qH9gGpfoogRU4BbGoq7KlZ3ouqVnEPUxof do1slc4ORuiNOFG/PvE7UrbUJYiIKMBdHxZ3xhseUhRO8tjAM2GbxV8kn0kRgb/FB/HC TuyRJgoGJ7h1QjPyNCVK2MPhp+XTvhF3Coc8D9LbYtN1fqSyPeKAqzAo+8vKBzzXnJkR H/dg== X-Gm-Message-State: AOJu0Yz/VvMQ8NWAGPDWma3wYTXZ435ENa5wU+p2Q39yXxB92fZoA3KB 7YEzYVgv/XK+MAOMBbVbe3n0guQoMrgX87AM9rOEUXhWFC3T3luaGp6Eaj8oHVc5zlRS6pZqmB4 D X-Google-Smtp-Source: AGHT+IFr9yJtovwVKZJYpmk0AcA9IlnhtyEBLv+Ge5UtndyFYGIbdLJdpFx57SLcI3KK5VenYgjaaA== X-Received: by 2002:a05:6a21:9688:b0:1c0:f5be:a3ca with SMTP id adf61e73a8af0-1c472a94065mr4430143637.30.1721938336587; Thu, 25 Jul 2024 13:12:16 -0700 (PDT) Received: from mandiga.. ([2804:1b3:a7c1:1944:71e3:2ede:b2a5:f38e]) by smtp.gmail.com with ESMTPSA id 41be03b00d2f7-7a9f884d12csm1334994a12.55.2024.07.25.13.12.13 for (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Thu, 25 Jul 2024 13:12:14 -0700 (PDT) From: Adhemerval Zanella To: libc-alpha@sourceware.org Subject: [PATCH v4 4/4] elf: Add glibc.rtld.execstack Date: Thu, 25 Jul 2024 17:05:20 -0300 Message-ID: <20240725201159.3286231-5-adhemerval.zanella@linaro.org> X-Mailer: git-send-email 2.43.0 In-Reply-To: <20240725201159.3286231-1-adhemerval.zanella@linaro.org> References: <20240725201159.3286231-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 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 6218c45d41..76f7759e42 100644 --- a/NEWS +++ b/NEWS @@ -93,6 +93,10 @@ Major new features: of large writes. This behaviour is controlled by a new tunable x86_memset_non_temporal_threshold. +* 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 a555346492..4b6dcfb023 100644 --- a/elf/Makefile +++ b/elf/Makefile @@ -547,6 +547,11 @@ tests-execstack-yes = \ tests-execstack-static-yes = \ tst-execstack-prog-static # tests-execstack-static-yes +tests-execstack-special-yes = \ + $(objpfx)tst-execstack-needed-noexecstack.out \ + $(objpfx)tst-execstack-prog-noexecstack.out \ + $(objpfx)tst-execstack-prog-static-noexecstack.out \ + # tests-execstack-special-yes endif ifeq ($(have-depaudit),yes) tests += \ @@ -635,6 +640,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)tst-ldconfig-X.out \ @@ -1886,6 +1892,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 40ac5b3776..8e656296bb 100644 --- a/elf/dl-tunables.list +++ b/elf/dl-tunables.list @@ -135,6 +135,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 bfdf632e77..f367df7dd0 100644 --- a/elf/rtld.c +++ b/elf/rtld.c @@ -1670,6 +1670,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 0b1b2898c0..326c36930e 100644 --- a/manual/tunables.texi +++ b/manual/tunables.texi @@ -355,6 +355,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