]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Add glibc.rtld.execstack
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Thu, 28 Nov 2024 17:36:45 +0000 (14:36 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Tue, 31 Dec 2024 12:04:20 +0000 (09:04 -0300)
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.

Reviewed-by: Florian Weimer <fweimer@redhat.com>
NEWS
elf/Makefile
elf/dl-load.c
elf/dl-support.c
elf/dl-tunables.list
elf/rtld.c
elf/tst-rtld-list-tunables.exp
manual/tunables.texi

diff --git a/NEWS b/NEWS
index a93c8437852b84c7757ae836d4d066fb65c558d2..71ec9d482b88f117f52c3d33d8f764e62efeee9d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -62,6 +62,11 @@ Major new features:
   asinf, asinhf, atanf, atan2f, atanhf, coshf, sinhf, and tanhf from CORE-MATH
   project <https://core-math.gitlabpages.inria.fr/>.
 
+* A new tunable, glibc.rtld.execstack, can be used to control whether a
+  executable stacks is allowed from the main program, either implicitly due
+  to a mising GNU_STACK ELF header or explicit explicitly because of the
+  executable bit in GNU_STACK.  The default is to allow executable stacks.
+
 Deprecated and removed features, and other changes affecting compatibility:
 
 * The big-endian ARC port (arceb-linux-gnu) has been removed.
index cea48e9537d14dec851ed514bec4f48295917b49..4874d9b59e2b24172f360966b407c1004f96e583 100644 (file)
@@ -570,6 +570,13 @@ tests-execstack-yes = \
 tests-execstack-static-yes = \
   tst-execstack-prog-static
   # tests-execstack-static-yes
+ifeq (yes,$(run-built-tests))
+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 # $(run-built-tests)
 endif
 endif
 ifeq ($(have-depaudit),yes)
@@ -666,6 +673,7 @@ $(objpfx)tst-rtld-dash-dash.out: tst-rtld-dash-dash.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 \
@@ -1989,6 +1997,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
index a238ff4286124c2a53080327c7531e375511be5c..76430e26dae2569d7dd2c90e02391227daa6251f 100644 (file)
@@ -32,6 +32,7 @@
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <gnu/lib-names.h>
+#include <dl-tunables.h>
 
 /* 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
@@ -1317,7 +1318,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)
        errval = _dl_make_stack_executable (stack_endp);
       else
        errval = EINVAL;
index fe1f8c8f6a896d866a00097ec2b4084b929b0cf0..73fcd338922dd09c2f3b705d7d660b3a7e71e8d8 100644 (file)
@@ -45,6 +45,7 @@
 #include <dl-find_object.h>
 #include <array_length.h>
 #include <dl-symbol-redir-ifunc.h>
+#include <dl-tunables.h>
 
 extern char *__progname;
 char **_dl_argv = &__progname; /* This is checked for some error messages.  */
@@ -331,6 +332,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.  */
index 40ac5b3776afba56300d38a5f4c612f4bf6279c6..8e656296bb9621a80f033a70c5bcbd5a8938c22e 100644 (file)
@@ -135,6 +135,12 @@ glibc {
       maxval: 1
       default: 0
     }
+    execstack {
+      type: INT_32
+      minval: 0
+      maxval: 1
+      default: 1
+    }
   }
 
   mem {
index 5eb130be307fe7c4147898179bd4e9f51dfeccca..8dd0381985bd35665c55b857b858a1356f21c122 100644 (file)
@@ -1645,6 +1645,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 (_dl_rtld_map.l_info[DT_SONAME] != NULL
index db0e1c86e9b5d68f06ebebb77b579c5f4d2e5fbc..9f5990f3404d93e0c4271ad2439318df076498dc 100644 (file)
@@ -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]+)
index 0b1b2898c095e53c1c56a8fcb71c2e9e314c34a7..1ac58b9ae9e0ba93b80b890d8495ad58d322f1da 100644 (file)
@@ -355,6 +355,34 @@ 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 ABI flags (that might
+contain the executable bit) or the value of @code{PT_GNU_STACK} (if present)
+to define whether to mark the stack non-executable and if the program or
+any shared library dependency requires an executable stack the loader will
+change the main stack permission if kernel starts with a non-executable stack.
+
+The @code{glibc.rtld.execstack} can be used to control whether an executable
+stack is allowed from the main program.  Setting the value to @code{0} disables
+the ABI auto-negotiation (meaning no executable stacks even if the ABI or ELF
+header requires it), while @code{1} enables auto-negotiation (although the
+program might not need an executable stack).
+
+When executable stacks are not allowed, and if the main program requires it,
+the loader will fail with an error message.
+
+Some systems do not have separate page protection flags at the hardware
+level for read access and execute access (sometimes called read-implies-exec).
+This mode can also be enabled on certain systems where the hardware supports
+separate protection flags.  The @theglibc{} tunable configuration is independent
+of hardware capabilities and kernel configuration.
+
+@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
+main program does not require an executable stack at loading time.  This
+is enforced regardless of the tunable value.
+@end deftp
+
 @node Elision Tunables
 @section Elision Tunables
 @cindex elision tunables