mirror of git://sourceware.org/git/glibc.git
aarch64: Add LD_DEBUG=security to log BTI and GCS warnings
Introduce DL_DEBUG_SECURITY mask to enable messages related to loading modules that lack certain target-dependent hardening or security features. Use this mask for warnings related to AArch64 BTI and GCS. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
This commit is contained in:
parent
274441f62a
commit
99b8ec8fd4
|
|
@ -2430,10 +2430,12 @@ process_dl_debug (struct dl_main_state *state, const char *dl_debug)
|
|||
DL_DEBUG_SCOPES },
|
||||
{ LEN_AND_STR ("tls"), "display TLS structures processing",
|
||||
DL_DEBUG_TLS },
|
||||
{ LEN_AND_STR ("security"), "show security warnings for input files",
|
||||
DL_DEBUG_SECURITY },
|
||||
{ LEN_AND_STR ("all"), "all previous options combined",
|
||||
DL_DEBUG_LIBS | DL_DEBUG_RELOC | DL_DEBUG_FILES | DL_DEBUG_SYMBOLS
|
||||
| DL_DEBUG_BINDINGS | DL_DEBUG_VERSIONS | DL_DEBUG_IMPCALLS
|
||||
| DL_DEBUG_SCOPES | DL_DEBUG_TLS },
|
||||
| DL_DEBUG_SCOPES | DL_DEBUG_TLS | DL_DEBUG_SECURITY },
|
||||
{ LEN_AND_STR ("statistics"), "display relocation statistics",
|
||||
DL_DEBUG_STATISTICS },
|
||||
{ LEN_AND_STR ("unused"), "determined unused DSOs",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/sh
|
||||
# A script to run tests with LD_DEBUG=security and check
|
||||
# that output contains expected pattern.
|
||||
# Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
# This file is part of the GNU C Library.
|
||||
#
|
||||
# The GNU C Library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
# The GNU C Library is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# Lesser General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Lesser General Public
|
||||
# License along with the GNU C Library; if not, see
|
||||
# <https://www.gnu.org/licenses/>.
|
||||
|
||||
# Arguments are from Makefile:
|
||||
# Path to the current build folder
|
||||
objpfx="$1"
|
||||
# Test wrapper command line
|
||||
wrapper="$2"
|
||||
# Dynamic loader command line
|
||||
loader="$3"
|
||||
# Test environment variables
|
||||
runenv="$4"
|
||||
# Grep pattern to look for in the test output
|
||||
pattern="$5"
|
||||
# Path to the test executable to run
|
||||
program="$6"
|
||||
|
||||
output="${objpfx}`basename ${program}`.debug"
|
||||
rm -f "${output}".*
|
||||
|
||||
eval "${wrapper}" \
|
||||
LD_DEBUG=security LD_DEBUG_OUTPUT="${output}" ${runenv} \
|
||||
"${loader}" "${program}"
|
||||
rc=$?
|
||||
|
||||
if test $rc -eq 77; then
|
||||
echo "Test is not supported"
|
||||
rm -f "${output}".*
|
||||
exit 77
|
||||
fi
|
||||
|
||||
output=$(ls "${output}".*)
|
||||
cat "${output}"
|
||||
if ! grep -q "${pattern}" "${output}"; then
|
||||
echo "Could not find expected '${pattern}' in LD_DEBUG_OUTPUT file"
|
||||
exit 1
|
||||
fi
|
||||
rm -f "${output}"
|
||||
|
|
@ -392,6 +392,11 @@ Display information about Thread-Local Storage (TLS) handling, including TCB
|
|||
allocation, deallocation, and reuse. This is useful for debugging issues
|
||||
related to thread creation and lifecycle.
|
||||
|
||||
@item security
|
||||
Display security warnings that are related to loading binaries that lack
|
||||
certain target-dependent hardening features. This may be useful for audit
|
||||
purposes.
|
||||
|
||||
@item all
|
||||
All previous options combined.
|
||||
|
||||
|
|
|
|||
|
|
@ -97,6 +97,10 @@ tests += \
|
|||
tst-bti-dlopen-imm \
|
||||
tst-bti-dlopen-prot \
|
||||
tst-bti-dlopen-transitive \
|
||||
tst-bti-ld-debug-both \
|
||||
tst-bti-ld-debug-dlopen \
|
||||
tst-bti-ld-debug-exe \
|
||||
tst-bti-ld-debug-shared \
|
||||
tst-bti-permissive-dlopen \
|
||||
tst-bti-permissive-imm \
|
||||
tst-bti-permissive-transitive \
|
||||
|
|
@ -115,8 +119,12 @@ $(objpfx)tst-bti-dep-prot: $(objpfx)tst-bti-mod-prot.so
|
|||
$(objpfx)tst-bti-mod.so: $(objpfx)tst-bti-mod-unprot.so
|
||||
$(objpfx)tst-bti-permissive-imm: $(objpfx)tst-bti-mod-unprot.so
|
||||
$(objpfx)tst-bti-permissive-transitive: $(objpfx)tst-bti-mod.so
|
||||
$(objpfx)tst-bti-ld-debug-shared: $(objpfx)tst-bti-mod.so
|
||||
$(objpfx)tst-bti-ld-debug-both: $(objpfx)tst-bti-mod-unprot.so
|
||||
|
||||
CFLAGS-tst-bti-abort-unprot.o += -mbranch-protection=none
|
||||
CFLAGS-tst-bti-ld-debug-exe.o += -mbranch-protection=none
|
||||
CFLAGS-tst-bti-ld-debug-both.o += -mbranch-protection=none
|
||||
CFLAGS-tst-bti-mod-unprot.os += -mbranch-protection=none
|
||||
|
||||
tst-bti-abort-imm-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_bti=1
|
||||
|
|
@ -148,6 +156,12 @@ tests-static += \
|
|||
tst-bti-abort-static-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_bti=1
|
||||
CFLAGS-tst-bti-abort-static.o += -mbranch-protection=none
|
||||
|
||||
$(objpfx)tst-bti-ld-debug-%.out: $(..)elf/tst-dl-debug-protect.sh $(objpfx)tst-bti-ld-debug-%
|
||||
$(SHELL) $< $(objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \
|
||||
'$(run-program-env) GLIBC_TUNABLES=glibc.cpu.aarch64_bti=0' \
|
||||
'security: not compatible with AArch64 BTI: $(objpfx)' \
|
||||
$(objpfx)tst-bti-ld-debug-$* > $@; $(evaluate-test)
|
||||
|
||||
endif # ifeq (yes,$(have-test-bti))
|
||||
|
||||
endif
|
||||
|
|
|
|||
|
|
@ -85,6 +85,16 @@ bti_failed (struct link_map *l, const char *program)
|
|||
"failed to turn on BTI protection");
|
||||
}
|
||||
|
||||
static void
|
||||
bti_warning (struct link_map *l, const char *program)
|
||||
{
|
||||
if (l->l_name[0] != '\0')
|
||||
_dl_debug_printf ("security: not compatible with AArch64 BTI: %s\n",
|
||||
l->l_name);
|
||||
else if (__glibc_likely (program != NULL))
|
||||
_dl_debug_printf ("security: not compatible with AArch64 BTI: %s\n",
|
||||
program);
|
||||
}
|
||||
|
||||
/* Enable BTI for L and its dependencies. */
|
||||
|
||||
|
|
@ -112,7 +122,12 @@ _dl_bti_check (struct link_map *l, const char *program)
|
|||
if (is_rtld_link_map (dep->l_real))
|
||||
continue;
|
||||
#endif
|
||||
if (enforce_bti && !dep->l_mach.bti)
|
||||
bti_failed (dep, program);
|
||||
if (!dep->l_mach.bti)
|
||||
{
|
||||
if (enforce_bti)
|
||||
bti_failed (dep, program);
|
||||
else if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SECURITY))
|
||||
bti_warning (dep, program);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,6 +50,17 @@ fail (struct link_map *l, const char *program)
|
|||
_dl_signal_error (0, l->l_name, "dlopen", "not GCS compatible");
|
||||
}
|
||||
|
||||
static void
|
||||
warn (struct link_map *l, const char *program)
|
||||
{
|
||||
if (l->l_name[0] != '\0')
|
||||
_dl_debug_printf ("security: not compatible with AArch64 GCS: %s\n",
|
||||
l->l_name);
|
||||
else if (__glibc_likely (program != NULL))
|
||||
_dl_debug_printf ("security: not compatible with AArch64 GCS: %s\n",
|
||||
program);
|
||||
}
|
||||
|
||||
static void
|
||||
unsupported (void)
|
||||
{
|
||||
|
|
@ -58,7 +69,7 @@ unsupported (void)
|
|||
|
||||
/* This function is called only when binary markings are not
|
||||
ignored and GCS is supposed to be enabled. This occurs
|
||||
for the GCS_POLICY_ENFORCED and GCS_POLICY_ENFORCED policies. */
|
||||
for the GCS_POLICY_ENFORCED and GCS_POLICY_OPTIONAL policies. */
|
||||
static bool
|
||||
check_gcs (struct link_map *l, const char *program, bool enforced)
|
||||
{
|
||||
|
|
@ -70,6 +81,9 @@ check_gcs (struct link_map *l, const char *program, bool enforced)
|
|||
/* Binary is marked, all good. */
|
||||
if (l->l_mach.gcs)
|
||||
return true;
|
||||
/* Extra logging requested, print path to failed binary. */
|
||||
if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_SECURITY))
|
||||
warn (l, program);
|
||||
/* Binary is not marked and loaded via dlopen: abort. */
|
||||
if (program == NULL)
|
||||
fail (l, program);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,3 @@
|
|||
/* This LD_DEBUG warning test allows to test a case when both the exe and
|
||||
one of its dependencies are not marked with BTI. */
|
||||
#include "tst-bti-skeleton.c"
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* Test that when BTI is not enforced an LD_DEBUG warning is printed
|
||||
when a library that does not have BTI marking is loaded via dlopen. */
|
||||
#define TEST_BTI_DLOPEN_MODULE "tst-bti-mod-unprot.so"
|
||||
#define TEST_BTI_EXPECT_DLOPEN 1
|
||||
#include "tst-bti-skeleton-dlopen.c"
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
/* Simple test for an executable without BTI marking.
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <stdio.h>
|
||||
#include <sys/auxv.h>
|
||||
#include <sys/signal.h>
|
||||
|
||||
#include <support/check.h>
|
||||
#include <support/test-driver.h>
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
unsigned long hwcap2 = getauxval (AT_HWCAP2);
|
||||
if ((hwcap2 & HWCAP2_BTI) == 0)
|
||||
FAIL_UNSUPPORTED ("BTI is not supported by this system");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
/* Test that when BTI is not enforced an LD_DEBUG warning is printed
|
||||
when one of the shared library dependencies does not have BTI marking. */
|
||||
#include "tst-bti-skeleton.c"
|
||||
|
|
@ -526,6 +526,7 @@ struct rtld_global_ro
|
|||
/* DL_DEBUG_HELP is only used internally. */
|
||||
#define DL_DEBUG_HELP (1 << 10)
|
||||
#define DL_DEBUG_TLS (1 << 11)
|
||||
#define DL_DEBUG_SECURITY (1 << 12)
|
||||
|
||||
/* Platform name. */
|
||||
EXTERN const char *_dl_platform;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,10 @@ gcs-tests-dynamic = \
|
|||
tst-gcs-dlopen-override \
|
||||
tst-gcs-enforced \
|
||||
tst-gcs-enforced-abort \
|
||||
tst-gcs-ld-debug-both \
|
||||
tst-gcs-ld-debug-dlopen \
|
||||
tst-gcs-ld-debug-exe \
|
||||
tst-gcs-ld-debug-shared \
|
||||
tst-gcs-noreturn \
|
||||
tst-gcs-optional-off \
|
||||
tst-gcs-optional-on \
|
||||
|
|
@ -73,10 +77,9 @@ LDFLAGS-tst-gcs-optional-on += -Wl,-z,gcs=always
|
|||
LDFLAGS-tst-gcs-optional-off += -Wl,-z,gcs=never
|
||||
LDFLAGS-tst-gcs-override += -Wl,-z,gcs=never
|
||||
|
||||
CFLAGS-tst-gcs-enforced-static-abort.o += -mbranch-protection=none
|
||||
|
||||
LDFLAGS-tst-gcs-disabled-static += -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-enforced-static += -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-enforced-static-abort += -Wl,-z,gcs=never
|
||||
LDFLAGS-tst-gcs-optional-static-on += -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-optional-static-off += -Wl,-z,gcs=never
|
||||
LDFLAGS-tst-gcs-override-static += -Wl,-z,gcs=never
|
||||
|
|
@ -90,7 +93,7 @@ tst-gcs-override-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=3
|
|||
|
||||
tst-gcs-disabled-static-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=0
|
||||
tst-gcs-enforced-static-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=1
|
||||
tst-gcs-enforced-static-abort-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=1:glibc.cpu.aarch64_bti=0
|
||||
tst-gcs-enforced-static-abort-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=1
|
||||
tst-gcs-optional-static-on-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=2
|
||||
tst-gcs-optional-static-off-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=2
|
||||
tst-gcs-override-static-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=3
|
||||
|
|
@ -103,6 +106,9 @@ LDFLAGS-tst-gcs-shared-enforced-abort = -Wl,-z,gcs=always
|
|||
LDFLAGS-tst-gcs-shared-optional = -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-shared-override = -Wl,-z,gcs=always
|
||||
|
||||
LDFLAGS-tst-gcs-ld-debug-shared = -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-ld-debug-dlopen = -Wl,-z,gcs=always
|
||||
|
||||
modules-names += \
|
||||
tst-gcs-mod1 \
|
||||
tst-gcs-mod2 \
|
||||
|
|
@ -114,6 +120,8 @@ $(objpfx)tst-gcs-shared-enforced-abort: $(objpfx)tst-gcs-mod1.so $(objpfx)tst-gc
|
|||
$(objpfx)tst-gcs-shared-optional: $(objpfx)tst-gcs-mod1.so $(objpfx)tst-gcs-mod3.so
|
||||
$(objpfx)tst-gcs-shared-override: $(objpfx)tst-gcs-mod1.so $(objpfx)tst-gcs-mod3.so
|
||||
$(objpfx)tst-gcs-mod1.so: $(objpfx)tst-gcs-mod2.so
|
||||
$(objpfx)tst-gcs-ld-debug-both: $(objpfx)tst-gcs-mod2.so
|
||||
$(objpfx)tst-gcs-ld-debug-shared: $(objpfx)tst-gcs-mod1.so $(objpfx)tst-gcs-mod3.so
|
||||
|
||||
tst-gcs-shared-disabled-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=0
|
||||
tst-gcs-shared-enforced-abort-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=1
|
||||
|
|
@ -125,6 +133,8 @@ LDFLAGS-tst-gcs-dlopen-enforced = -Wl,-z,gcs=always
|
|||
LDFLAGS-tst-gcs-dlopen-optional-on = -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-dlopen-optional-off = -Wl,-z,gcs=never
|
||||
LDFLAGS-tst-gcs-dlopen-override = -Wl,-z,gcs=always
|
||||
LDFLAGS-tst-gcs-ld-debug-exe = -Wl,-z,gcs=never
|
||||
LDFLAGS-tst-gcs-ld-debug-both = -Wl,-z,gcs=never
|
||||
|
||||
tst-gcs-dlopen-disabled-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=0
|
||||
tst-gcs-dlopen-enforced-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=1
|
||||
|
|
@ -137,11 +147,18 @@ $(objpfx)tst-gcs-dlopen-enforced.out: $(objpfx)tst-gcs-mod2.so
|
|||
$(objpfx)tst-gcs-dlopen-optional-on.out: $(objpfx)tst-gcs-mod2.so
|
||||
$(objpfx)tst-gcs-dlopen-optional-off.out: $(objpfx)tst-gcs-mod2.so
|
||||
$(objpfx)tst-gcs-dlopen-override.out: $(objpfx)tst-gcs-mod2.so
|
||||
$(objpfx)tst-gcs-ld-debug-dlopen.out: $(objpfx)tst-gcs-mod2.so
|
||||
|
||||
LDFLAGS-tst-gcs-noreturn = -Wl,-z,gcs=always
|
||||
|
||||
tst-gcs-noreturn-ENV = GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=0
|
||||
|
||||
$(objpfx)tst-gcs-ld-debug-%.out: $(..)elf/tst-dl-debug-protect.sh $(objpfx)tst-gcs-ld-debug-%
|
||||
$(SHELL) $< $(objpfx) '$(test-wrapper-env)' '$(rtld-prefix)' \
|
||||
'$(run-program-env) GLIBC_TUNABLES=glibc.cpu.aarch64_gcs=2' \
|
||||
'security: not compatible with AArch64 GCS: $(objpfx)' \
|
||||
$(objpfx)tst-gcs-ld-debug-$* > $@; $(evaluate-test)
|
||||
|
||||
endif # ifeq ($(have-test-gcs),yes)
|
||||
|
||||
endif # ifeq ($(subdir),misc)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,38 @@
|
|||
/* Test that when GCS is optional an LD_DEBUG warning is printed when
|
||||
both the executable and its shared library dependency do not have
|
||||
GCS marking.
|
||||
Copyright (C) 2026 Free Software Foundation, Inc.
|
||||
This file is part of the GNU C Library.
|
||||
|
||||
The GNU C Library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
The GNU C Library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with the GNU C Library; if not, see
|
||||
<https://www.gnu.org/licenses/>. */
|
||||
|
||||
#include "tst-gcs-helper.h"
|
||||
|
||||
/* Defined in tst-gcs-mod2.c. */
|
||||
extern int fun2 (void);
|
||||
|
||||
static int
|
||||
do_test (void)
|
||||
{
|
||||
/* Check if GCS could possible by enabled. */
|
||||
if (!(getauxval (AT_HWCAP) & HWCAP_GCS))
|
||||
FAIL_UNSUPPORTED ("kernel or CPU does not support GCS");
|
||||
bool gcs_enabled = __check_gcs_status ();
|
||||
puts (gcs_enabled ? "GCS enabled" : "GCS not enabled");
|
||||
TEST_VERIFY (!gcs_enabled);
|
||||
return fun2();
|
||||
}
|
||||
|
||||
#include <support/test-driver.c>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
/* Test that when GCS is optional an LD_DEBUG warning is printed when
|
||||
a library that does not have GCS marking is loaded via dlopen. */
|
||||
#define TEST_GCS_EXPECT_ENABLED 0
|
||||
#define TEST_GCS_EXPECT_DLOPEN 0
|
||||
#include "tst-gcs-dlopen.c"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/* Test that when GCS is optional an LD_DEBUG warning is printed when
|
||||
the executable does not have GCS marking. */
|
||||
#define TEST_GCS_EXPECT_ENABLED 0
|
||||
#include "tst-gcs-skeleton.c"
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
/* Test that when GCS is optional an LD_DEBUG warning is printed when
|
||||
one of the shared library dependencies does not have GCS marking. */
|
||||
#define TEST_GCS_EXPECT_ENABLED 0
|
||||
#include "tst-gcs-shared.c"
|
||||
Loading…
Reference in New Issue