elf: Add glibc.rtld.execstack

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>
This commit is contained in:
Adhemerval Zanella 2024-11-28 14:36:45 -03:00
parent c9540704ac
commit 58272284b6
8 changed files with 96 additions and 1 deletions

5
NEWS
View File

@ -62,6 +62,11 @@ Major new features:
asinf, asinhf, atanf, atan2f, atanhf, coshf, sinhf, and tanhf from CORE-MATH asinf, asinhf, atanf, atan2f, atanhf, coshf, sinhf, and tanhf from CORE-MATH
project <https://core-math.gitlabpages.inria.fr/>. 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: Deprecated and removed features, and other changes affecting compatibility:
* The big-endian ARC port (arceb-linux-gnu) has been removed. * The big-endian ARC port (arceb-linux-gnu) has been removed.

View File

@ -570,6 +570,13 @@ tests-execstack-yes = \
tests-execstack-static-yes = \ tests-execstack-static-yes = \
tst-execstack-prog-static tst-execstack-prog-static
# tests-execstack-static-yes # 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
endif endif
ifeq ($(have-depaudit),yes) 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 += $(tests-execstack-$(have-z-execstack))
tests-static+= $(tests-execstack-static-$(have-z-execstack)) tests-static+= $(tests-execstack-static-$(have-z-execstack))
tests-special += $(tests-execstack-special-$(have-z-execstack))
ifeq ($(run-built-tests),yes) ifeq ($(run-built-tests),yes)
tests-special += \ tests-special += \
$(objpfx)tst-ldconfig-X.out \ $(objpfx)tst-ldconfig-X.out \
@ -1989,6 +1997,42 @@ CFLAGS-tst-execstack-mod.c += -Wno-trampolines
LDFLAGS-tst-execstack-prog-static = -Wl,-z,execstack LDFLAGS-tst-execstack-prog-static = -Wl,-z,execstack
CFLAGS-tst-execstack-prog-static.c += -Wno-trampolines 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 endif
LDFLAGS-tst-array2 = -Wl,--no-as-needed LDFLAGS-tst-array2 = -Wl,--no-as-needed

View File

@ -32,6 +32,7 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/types.h> #include <sys/types.h>
#include <gnu/lib-names.h> #include <gnu/lib-names.h>
#include <dl-tunables.h>
/* Type for the buffer we put the ELF header and hopefully the program /* 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 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 /* The stack is presently not executable, but this module
requires that it be executable. Only tries to change the requires that it be executable. Only tries to change the
stack protection during process startup. */ 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); errval = _dl_make_stack_executable (stack_endp);
else else
errval = EINVAL; errval = EINVAL;

View File

@ -45,6 +45,7 @@
#include <dl-find_object.h> #include <dl-find_object.h>
#include <array_length.h> #include <array_length.h>
#include <dl-symbol-redir-ifunc.h> #include <dl-symbol-redir-ifunc.h>
#include <dl-tunables.h>
extern char *__progname; extern char *__progname;
char **_dl_argv = &__progname; /* This is checked for some error messages. */ char **_dl_argv = &__progname; /* This is checked for some error messages. */
@ -331,6 +332,10 @@ _dl_non_dynamic_init (void)
break; 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); call_function_static_weak (_dl_find_object_init);
/* Setup relro on the binary itself. */ /* Setup relro on the binary itself. */

View File

@ -135,6 +135,12 @@ glibc {
maxval: 1 maxval: 1
default: 0 default: 0
} }
execstack {
type: INT_32
minval: 0
maxval: 1
default: 1
}
} }
mem { mem {

View File

@ -1645,6 +1645,10 @@ dl_main (const ElfW(Phdr) *phdr,
bool has_interp = rtld_setup_main_map (main_map); 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 /* If the current libname is different from the SONAME, add the
latter as well. */ latter as well. */
if (_dl_rtld_map.l_info[DT_SONAME] != NULL if (_dl_rtld_map.l_info[DT_SONAME] != NULL

View 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.malloc.trim_threshold: 0x0 (min: 0x0, max: 0x[f]+)
glibc.rtld.dynamic_sort: 2 (min: 1, max: 2) glibc.rtld.dynamic_sort: 2 (min: 1, max: 2)
glibc.rtld.enable_secure: 0 (min: 0, max: 1) 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.nns: 0x4 (min: 0x1, max: 0x10)
glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+) glibc.rtld.optional_static_tls: 0x200 (min: 0x0, max: 0x[f]+)

View 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}. The default value of this tunable is @samp{0}.
@end deftp @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 @node Elision Tunables
@section Elision Tunables @section Elision Tunables
@cindex elision tunables @cindex elision tunables