[glibc/azanella/aarch64-memtag] aarch64: Add initial Memtag ABI Extension to ELF (stack only)
Adhemerval Zanella via Glibc-cvs <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=8db1bd3af55aa2438642de773fb4a805f5972edf commit 8db1bd3af55aa2438642de773fb4a805f5972edf Author: Adhemerval Zanella <[email protected]> Date: Mon Aug 24 20:43:28 2026 -0300 aarch64: Add initial Memtag ABI Extension to ELF (stack only) This patch introduces initial support for the AArch64 Memory Tagging Extension (MTE) in the dynamic linker, implementing stack tagging (DT_AARCH64_MEMTAG_STACK) and MTE mode configuration (DT_AARCH64_MEMTAG_MODE) as defined in the Memtag ABI Extension for ELF64 [1]. The feature is guarded by a new configure option, --enable-aarch64-memtag-abi, and it is disabled by default. Semantics: * The MTE stack protection is enabled if and only if the main executable (PDE/PIE) is marked with DT_AARCH64_MEMTAG_STACK. * If the main executable is not marked and any dependency requires MTE stack protection, the dynamic loader aborts at startup. * Any unsupported marking (DT_AARCH64_MEMTAG_HEAP or DT_AARCH64_MEMTAG_GLOBALS), and any invalid or inconsistent (orphan) set of DT_AARCH64_MEMTAG_xxx dynamic tags, on either the main executable or any dependency, aborts the startup. * The MTE execution mode is defined by DT_AARCH64_MEMTAG_MODE of the main executable; if absent, asynchronous mode is used. * If the main executable did not enable the MTE stack protection at startup, any subsequent dlopen of a shared object that requires it is rejected to prevent unsafe memory access. Static PDEs are not supported since they carry no dynamic tags. [1] https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst Diff: --- INSTALL | 9 + NEWS | 5 + config.h.in | 3 + config.make.in | 2 + configure | 24 +++ configure.ac | 14 ++ elf/Versions | 7 + elf/elf.h | 7 +- manual/install.texi | 23 +++ sysdeps/aarch64/Makefile | 1 + sysdeps/aarch64/configure | 191 ++++++++++++++++++++ sysdeps/aarch64/configure.ac | 51 ++++++ sysdeps/aarch64/cpu-features.h | 7 + sysdeps/aarch64/dl-mte.c | 199 +++++++++++++++++++++ sysdeps/aarch64/dl-mte.h | 35 ++++ sysdeps/aarch64/dl-prop.h | 7 + sysdeps/aarch64/dl-start.S | 5 + sysdeps/unix/sysv/linux/aarch64/Makefile | 106 +++++++++++ sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c | 31 ++++ sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c | 14 ++ .../unix/sysv/linux/aarch64/tst-memtag-abort-dep.c | 32 ++++ .../sysv/linux/aarch64/tst-memtag-abort-heap.c | 28 +++ .../sysv/linux/aarch64/tst-memtag-abort-orphan.c | 29 +++ .../unix/sysv/linux/aarch64/tst-memtag-abort.sh | 40 +++++ .../sysv/linux/aarch64/tst-memtag-dlopen-orphan.c | 47 +++++ sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod1.c | 1 + sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod2.c | 1 + sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod3.c | 10 ++ sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod4.c | 8 + .../sysv/linux/aarch64/tst-memtag-stack-async.c | 2 + .../sysv/linux/aarch64/tst-memtag-stack-dep1.c | 3 + .../sysv/linux/aarch64/tst-memtag-stack-dep2.c | 3 + .../linux/aarch64/tst-memtag-stack-dlopen-dep.c | 45 +++++ .../aarch64/tst-memtag-stack-dlopen-enabled.c | 57 ++++++ .../sysv/linux/aarch64/tst-memtag-stack-dlopen.c | 45 +++++ .../sysv/linux/aarch64/tst-memtag-stack-skeleton.c | 72 ++++++++ .../sysv/linux/aarch64/tst-memtag-stack-sync.c | 2 + .../sysv/linux/aarch64/tst-memtag-test-skeleton.c | 85 +++++++++ sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h | 57 ++++++ 39 files changed, 1307 insertions(+), 1 deletion(-) diff --git a/INSTALL b/INSTALL index bafae84f75..1c91b75c30 100644 --- a/INSTALL +++ b/INSTALL @@ -279,6 +279,15 @@ passed to 'configure'. For example: Default is to disable SFrame support. +'--enable-aarch64-memtag-abi' + Enable initial support for the AArch64 Memory Tagging Extension + (MTE) ABI. The option implements stack tagging + (DT_AARCH64_MEMTAG_STACK) and MTE execution mode configuration + (DT_AARCH64_MEMTAG_MODE) as defined in the Memtag ABI Extension for + ELF64. + + This option is disabled by default and only supported on aarch64. + To build the library and related programs, type 'make'. This will produce a lot of output, some of which may look like errors from 'make' but aren't. Look for error messages from 'make' containing '***'. diff --git a/NEWS b/NEWS index 56c6b581df..1a660a1b52 100644 --- a/NEWS +++ b/NEWS @@ -90,6 +90,11 @@ Major new features: * A new locale has been added: hrx_BR (Hunsrik language spoken in Brazil). +* AArch64 now has initial support for the Memtag ABI Extension for ELF64, + specifically handling stack tagging (DT_AARCH64_MEMTAG_STACK) and execution + mode configuration (DT_AARCH64_MEMTAG_MODE). It can be enabled with the + new --enable-aarch64-memtag-abi configure option. + Deprecated and removed features, and other changes affecting compatibility: * Although malloc and related functions currently return pointers diff --git a/config.h.in b/config.h.in index 17bdd30570..5db3856ea6 100644 --- a/config.h.in +++ b/config.h.in @@ -182,6 +182,9 @@ /* Define if inlined system calls are available. */ #undef HAVE_INLINED_SYSCALLS +/* Define if AArch64 Memtag ABI extension should be enabled. */ +#undef USE_AARCH64_MEMTAG_ABI + /* Package description. */ #undef PKGVERSION diff --git a/config.make.in b/config.make.in index 0ddc24a589..f8c7fcca2d 100644 --- a/config.make.in +++ b/config.make.in @@ -78,6 +78,8 @@ multi-arch = @multi_arch@ mach-interface-list = @mach_interface_list@ +aarch64-memtag-abi = @aarch64_memtag_abi@ + # Configuration options. build-shared = @shared@ build-profile = @profile@ diff --git a/configure b/configure index 604279533a..77f43982e8 100755 --- a/configure +++ b/configure @@ -702,6 +702,7 @@ INSTALL_PROGRAM base_machine build_pt_chown build_nscd +aarch64_memtag_abi enable_werror force_install bindnow @@ -808,6 +809,7 @@ enable_maintainer_mode enable_kernel enable_werror enable_multi_arch +enable_aarch64_memtag_abi enable_systemtap enable_build_nscd enable_nscd @@ -1485,6 +1487,9 @@ Optional Features: --disable-werror do not build with -Werror --enable-multi-arch enable single DSO with optimizations for multiple architectures + --enable-aarch64-memtag-abi + enable AArch64 Memtag ABI Extension for ELF + [default=no] --enable-systemtap enable systemtap static probe points [default=no] --disable-build-nscd disable building and installing the nscd daemon --disable-nscd library functions will not contact the nscd daemon @@ -4668,6 +4673,25 @@ esac fi +# Check whether --enable-aarch64-memtag-abi was given. +if test ${enable_aarch64_memtag_abi+y} +then : + enableval=$enable_aarch64_memtag_abi; aarch64_memtag_abi=$enableval +else case e in #( + e) aarch64_memtag_abi=no ;; +esac +fi + +if test "$aarch64_memtag_abi" = yes; then + case $host_cpu in + aarch64) + printf "%s\n" "#define USE_AARCH64_MEMTAG_ABI 1" >>confdefs.h + + ;; + esac +fi + + # Check whether --enable-systemtap was given. if test ${enable_systemtap+y} then : diff --git a/configure.ac b/configure.ac index 2fe7980fc9..357cc39432 100644 --- a/configure.ac +++ b/configure.ac @@ -315,6 +315,20 @@ AC_ARG_ENABLE([multi-arch], [multi_arch=$enableval], [multi_arch=default]) +AC_ARG_ENABLE([aarch64-memtag-abi], + AS_HELP_STRING([--enable-aarch64-memtag-abi], + [enable AArch64 Memtag ABI Extension for ELF @<:@default=no@:>@]), + [aarch64_memtag_abi=$enableval], + [aarch64_memtag_abi=no]) +if test "$aarch64_memtag_abi" = yes; then + case $host_cpu in + aarch64) + AC_DEFINE(USE_AARCH64_MEMTAG_ABI) + ;; + esac +fi +AC_SUBST(aarch64_memtag_abi) + AC_ARG_ENABLE([systemtap], [AS_HELP_STRING([--enable-systemtap], [enable systemtap static probe points @<:@default=no@:>@])], diff --git a/elf/Versions b/elf/Versions index 1591031da9..824ade26d9 100644 --- a/elf/Versions +++ b/elf/Versions @@ -28,6 +28,13 @@ libc { # by scripts/versions.awk. __placeholder_only_for_empty_version_map; } +%ifdef USE_AARCH64_MEMTAG_ABI + GLIBC_ABI_MEMTAG { + # This symbol is used only for empty version map and will be removed + # by scripts/versions.awk. + __placeholder_only_for_empty_version_map; + } +%endif GLIBC_PRIVATE { # functions used in other libraries __libc_early_init; diff --git a/elf/elf.h b/elf/elf.h index b482fcfb64..19f310bfa2 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -3041,7 +3041,12 @@ enum #define DT_AARCH64_BTI_PLT (DT_LOPROC + 1) #define DT_AARCH64_PAC_PLT (DT_LOPROC + 3) #define DT_AARCH64_VARIANT_PCS (DT_LOPROC + 5) -#define DT_AARCH64_NUM 6 +#define DT_AARCH64_MEMTAG_MODE (DT_LOPROC + 9) +#define DT_AARCH64_MEMTAG_HEAP (DT_LOPROC + 11) +#define DT_AARCH64_MEMTAG_STACK (DT_LOPROC + 12) +#define DT_AARCH64_MEMTAG_GLOBALS (DT_LOPROC + 13) +#define DT_AARCH64_MEMTAG_GLOBALSSZ (DT_LOPROC + 15) +#define DT_AARCH64_NUM 16 /* AArch64 specific values for the st_other field. */ #define STO_AARCH64_VARIANT_PCS 0x80 diff --git a/manual/install.texi b/manual/install.texi index fe4a368385..1c1f3a642c 100644 --- a/manual/install.texi +++ b/manual/install.texi @@ -309,6 +309,29 @@ Currently this is only supported on x86_64 and aarch64. The option enables SFrame support on @code{backtrace}. Default is to disable SFrame support. + +@item --enable-aarch64-memtag-abi +Enable initial support for the AArch64 Memory Tagging Extension (MTE) ABI. +The option implements stack tagging (@code{DT_AARCH64_MEMTAG_STACK}) and MTE +execution mode configuration (@code{DT_AARCH64_MEMTAG_MODE}) as defined in +the Memtag ABI Extension for ELF64. + +The MTE stack protection is enabled if and only if the main executable is +marked with @code{DT_AARCH64_MEMTAG_STACK}. If the main executable is not +marked and any dependency requires MTE stack protection, the program fails +at startup. The program also fails at startup if any object carries an +unsupported marking (@code{DT_AARCH64_MEMTAG_HEAP} or +@code{DT_AARCH64_MEMTAG_GLOBALS}), or an invalid or inconsistent set of +@code{DT_AARCH64_MEMTAG_xxx} dynamic tags. + +If the execution mode is not specified with @code{DT_AARCH64_MEMTAG_MODE}, +the asynchronous mode is used. + +If the main executable does not enable the MTE stack protection at startup, +any subsequent attempt to @code{dlopen} a shared object that requires it +will be rejected. + +This option is disabled by default and only supported on aarch64. @end table To build the library and related programs, type @code{make}. This will diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile index 87ce51c821..f046148091 100644 --- a/sysdeps/aarch64/Makefile +++ b/sysdeps/aarch64/Makefile @@ -4,6 +4,7 @@ ifeq ($(subdir),elf) sysdep-dl-routines += \ dl-bti \ dl-gcs \ + dl-mte \ # sysdep-dl-routines tests += \ diff --git a/sysdeps/aarch64/configure b/sysdeps/aarch64/configure index 44b833ea1d..19ab6d114a 100755 --- a/sysdeps/aarch64/configure +++ b/sysdeps/aarch64/configure @@ -484,3 +484,194 @@ else have-test-gcs = no" fi +# Check if compiler supports MEMTAG stack support. Also check if linker +# support the required options. + +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler supports -fsanitize=memtag-stack" >&5 +printf %s "checking if compiler supports -fsanitize=memtag-stack... " >&6; } +if test ${libc_cv_cc_memtag_stack+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) if { ac_try='${CC-cc} -Werror -march=armv8.5-a+memtag -fsanitize=memtag-stack -xc /dev/null -S -o /dev/null' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then : + libc_cv_cc_memtag_stack=yes +else case e in #( + e) libc_cv_cc_memtag_stack=no ;; +esac +fi ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_cc_memtag_stack" >&5 +printf "%s\n" "$libc_cv_cc_memtag_stack" >&6; } +if test "$TEST_CC" = "$CC"; then + libc_cv_test_cc_memtag_stack=$libc_cv_cc_memtag_stack +else + +saved_CC="$CC" +CC="$TEST_CC" +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if compiler supports -fsanitize=memtag-stack in testing" >&5 +printf %s "checking if compiler supports -fsanitize=memtag-stack in testing... " >&6; } +if test ${libc_cv_test_cc_memtag_stack+y} +then : + printf %s "(cached) " >&6 +else case e in #( + e) if { ac_try='${CC-cc} -Werror -march=armv8.5-a+memtag -fsanitize=memtag-stack -xc /dev/null -S -o /dev/null' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then : + libc_cv_test_cc_memtag_stack=yes +else case e in #( + e) libc_cv_test_cc_memtag_stack=no + ;; +esac +fi ;; +esac +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_cv_test_cc_memtag_stack" >&5 +printf "%s\n" "$libc_cv_test_cc_memtag_stack" >&6; } + +CC="$saved_CC" + +fi + +# lld requires the memtag-mode to be used along memtag-stack +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z memtag-stack" >&5 +printf %s "checking for linker that supports -z memtag-stack... " >&6; } +libc_linker_feature=no +cat > conftest.c <<EOF +int _start (void) { return 42; } +EOF +if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp + -Wl,--fatal-warnings -Wl,-z,memtag-stack -z memtag-mode=sync -nostdlib -nostartfiles + -fPIC -shared -o conftest.so conftest.c + 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then + if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp -Wl,--fatal-warnings -Wl,-z,memtag-stack -z memtag-mode=sync -nostdlib \ + -nostartfiles -fPIC -shared -o conftest.so conftest.c 2>&1 \ + | grep "warning: -z memtag-stack ignored" > /dev/null 2>&1; then + true + else + libc_linker_feature=yes + fi +fi +rm -f conftest* +if test $libc_linker_feature = yes; then + libc_cv_ld_memtag_stack=yes +else + libc_cv_ld_memtag_stack=no + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5 +printf "%s\n" "$libc_linker_feature" >&6; } +if test $libc_cv_test_cc_memtag_stack = yes && \ + test $libc_cv_ld_memtag_stack = yes +then + config_vars="$config_vars +have-test-memtag-stack = yes" +else + config_vars="$config_vars +have-test-memtag-stack = no" +fi + +# Check if linker supports Memtag Heap support. lld requires the memtag-mode. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z memtag-heap" >&5 +printf %s "checking for linker that supports -z memtag-heap... " >&6; } +libc_linker_feature=no +cat > conftest.c <<EOF +int _start (void) { return 42; } +EOF +if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp + -Wl,--fatal-warnings -Wl,-z,memtag-heap -z memtag-mode=sync -nostdlib -nostartfiles + -fPIC -shared -o conftest.so conftest.c + 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then + if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp -Wl,--fatal-warnings -Wl,-z,memtag-heap -z memtag-mode=sync -nostdlib \ + -nostartfiles -fPIC -shared -o conftest.so conftest.c 2>&1 \ + | grep "warning: -z memtag-heap ignored" > /dev/null 2>&1; then + true + else + libc_linker_feature=yes + fi +fi +rm -f conftest* +if test $libc_linker_feature = yes; then + libc_cv_ld_memtag_heap=yes +else + libc_cv_ld_memtag_heap=no + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5 +printf "%s\n" "$libc_linker_feature" >&6; } +if test $libc_cv_ld_memtag_heap = yes +then + config_vars="$config_vars +have-test-memtag-heap = yes" +else + config_vars="$config_vars +have-test-memtag-heap = no" +fi + +# Check if the linker accepts -z memtag-mode without any MTE feature option. +# BFD ld emits an orphan DT_AARCH64_MEMTAG_MODE dynamic tag in this case, +# which is used to check the inconsistent marking handling. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for linker that supports -z memtag-mode" >&5 +printf %s "checking for linker that supports -z memtag-mode... " >&6; } +libc_linker_feature=no +cat > conftest.c <<EOF +int _start (void) { return 42; } +EOF +if { ac_try='${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp + -Wl,--fatal-warnings -Wl,-z,memtag-mode=sync -nostdlib -nostartfiles + -fPIC -shared -o conftest.so conftest.c + 1>&5' + { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5 + (eval $ac_try) 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; } +then + if ${CC-cc} $CFLAGS $CPPFLAGS $LDFLAGS $no_ssp -Wl,--fatal-warnings -Wl,-z,memtag-mode=sync -nostdlib \ + -nostartfiles -fPIC -shared -o conftest.so conftest.c 2>&1 \ + | grep "warning: -z memtag-mode ignored" > /dev/null 2>&1; then + true + else + libc_linker_feature=yes + fi +fi +rm -f conftest* +if test $libc_linker_feature = yes; then + libc_cv_ld_memtag_mode_only=yes +else + libc_cv_ld_memtag_mode_only=no + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $libc_linker_feature" >&5 +printf "%s\n" "$libc_linker_feature" >&6; } +if test $libc_cv_ld_memtag_mode_only = yes +then + config_vars="$config_vars +have-test-memtag-mode-only = yes" +else + config_vars="$config_vars +have-test-memtag-mode-only = no" +fi + diff --git a/sysdeps/aarch64/configure.ac b/sysdeps/aarch64/configure.ac index b9981861f6..0614be9c0c 100644 --- a/sysdeps/aarch64/configure.ac +++ b/sysdeps/aarch64/configure.ac @@ -105,3 +105,54 @@ then else LIBC_CONFIG_VAR([have-test-gcs], [no]) fi + +# Check if compiler supports MEMTAG stack support. Also check if linker +# support the required options. +LIBC_TRY_CC_AND_TEST_CC_OPTION( + [if compiler supports -fsanitize=memtag-stack], + [-Werror -march=armv8.5-a+memtag -fsanitize=memtag-stack], + libc_cv_cc_memtag_stack, + [libc_cv_cc_memtag_stack=yes], + [libc_cv_cc_memtag_stack=no], + libc_cv_test_cc_memtag_stack, + [libc_cv_test_cc_memtag_stack=yes], + [libc_cv_test_cc_memtag_stack=no] +) +# lld requires the memtag-mode to be used along memtag-stack +LIBC_LINKER_FEATURE( + [-z memtag-stack], [-Wl,--fatal-warnings -Wl,-z,memtag-stack -z memtag-mode=sync], + [libc_cv_ld_memtag_stack=yes], [libc_cv_ld_memtag_stack=no] +) +if test $libc_cv_test_cc_memtag_stack = yes && \ + test $libc_cv_ld_memtag_stack = yes +then + LIBC_CONFIG_VAR([have-test-memtag-stack], [yes]) +else + LIBC_CONFIG_VAR([have-test-memtag-stack], [no]) +fi + +# Check if linker supports Memtag Heap support. lld requires the memtag-mode. +LIBC_LINKER_FEATURE( + [-z memtag-heap], [-Wl,--fatal-warnings -Wl,-z,memtag-heap -z memtag-mode=sync], + [libc_cv_ld_memtag_heap=yes], [libc_cv_ld_memtag_heap=no] +) +if test $libc_cv_ld_memtag_heap = yes +then + LIBC_CONFIG_VAR([have-test-memtag-heap], [yes]) +else + LIBC_CONFIG_VAR([have-test-memtag-heap], [no]) +fi + +# Check if the linker accepts -z memtag-mode without any MTE feature option. +# BFD ld emits an orphan DT_AARCH64_MEMTAG_MODE dynamic tag in this case, +# which is used to check the inconsistent marking handling. +LIBC_LINKER_FEATURE( + [-z memtag-mode], [-Wl,--fatal-warnings -Wl,-z,memtag-mode=sync], + [libc_cv_ld_memtag_mode_only=yes], [libc_cv_ld_memtag_mode_only=no] +) +if test $libc_cv_ld_memtag_mode_only = yes +then + LIBC_CONFIG_VAR([have-test-memtag-mode-only], [yes]) +else + LIBC_CONFIG_VAR([have-test-memtag-mode-only], [no]) +fi diff --git a/sysdeps/aarch64/cpu-features.h b/sysdeps/aarch64/cpu-features.h index b7dab3dff4..fe8b58c884 100644 --- a/sysdeps/aarch64/cpu-features.h +++ b/sysdeps/aarch64/cpu-features.h @@ -59,6 +59,13 @@ enum { BTI_CHECK_ENFORCED = 1, }; +enum { + MTE_MODE_SYNC = 0x1, + MTE_MODE_ASYNC = 0x2, + MTE_MODE_MASK = MTE_MODE_SYNC | MTE_MODE_ASYNC, + MTE_STACK = 0x4, +}; + struct cpu_features { uint64_t midr_el1; diff --git a/sysdeps/aarch64/dl-mte.c b/sysdeps/aarch64/dl-mte.c new file mode 100644 index 0000000000..27f6190b9f --- /dev/null +++ b/sysdeps/aarch64/dl-mte.c @@ -0,0 +1,199 @@ +/* AArch64 Memtag ABI Extension for ELF support. + Copyright (C) 2026 Free Software Foundation, Inc. + + 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 <ldsodefs.h> +#include <sys/auxv.h> +#include <dl-mte.h> +#include <dl-prop.h> + +#ifdef USE_AARCH64_MEMTAG_ABI + +/* The maximal set of permitted tags that the MTE random tag generation + instruction may use. */ +# define MTE_ALLOWED_TAGS (0xfffe << PR_MTE_TAG_SHIFT) + +# define DT_AARCH64(x) (DT_AARCH64_##x - DT_LOPROC + DT_NUM) + +/* Parsed view of the DT_AARCH64_MEMTAG_xxx dynamic tags of an object. */ +struct memtag_marking +{ + /* DT_AARCH64_MEMTAG_MODE, valid iff MODE_PRESENT. */ + uint64_t mode; + bool mode_present; + /* The object requires the respective MTE protection. */ + bool stack; + bool heap; + bool globals; + /* A tag has a value outside the ones defined by the ABI. */ + bool invalid; + /* MEMTAG_MODE without any protection being requested, or only one of + MEMTAG_GLOBALS and MEMTAG_GLOBALSSZ. */ + bool orphan; +}; + +static struct memtag_marking +read_marking (const struct link_map *l) +{ + struct memtag_marking m = { 0 }; + const ElfW(Dyn) *d; + + bool stack_present = false; + bool heap_present = false; + + if ((d = l->l_info[DT_AARCH64 (MEMTAG_MODE)]) != NULL) + { + m.mode_present = true; + m.mode = d->d_un.d_val; + if (m.mode > 1) + m.invalid = true; + } + + if ((d = l->l_info[DT_AARCH64 (MEMTAG_STACK)]) != NULL) + { + stack_present = true; + if (d->d_un.d_val > 1) + m.invalid = true; + m.stack = d->d_un.d_val != 0; + } + + if ((d = l->l_info[DT_AARCH64 (MEMTAG_HEAP)]) != NULL) + { + heap_present = true; + if (d->d_un.d_val > 1) + m.invalid = true; + m.heap = d->d_un.d_val != 0; + } + + bool globals_present = l->l_info[DT_AARCH64 (MEMTAG_GLOBALS)] != NULL; + bool globalssz_present = l->l_info[DT_AARCH64 (MEMTAG_GLOBALSSZ)] != NULL; + m.globals = globals_present; + + if (globals_present != globalssz_present) + m.orphan = true; + if (m.mode_present && !stack_present && !heap_present && !globals_present) + m.orphan = true; + + return m; +} + +/* PROGRAM is non-NULL during program startup, where any failure is fatal; + otherwise the object comes from dlopen and the error can be handled by + the caller. */ +static void _Noreturn +fail (const struct link_map *l, const char *program, const char *reason) +{ + if (program == NULL) + _dl_signal_error (0, l->l_name, "dlopen", reason); + + if (l->l_name[0] != '\0') + _dl_fatal_printf ("%s: error: %s: %s\n", program, l->l_name, reason); + _dl_fatal_printf ("%s: error: %s\n", program, reason); +} + +static void +check_marking (const struct link_map *l, const char *program, + const struct memtag_marking *m) +{ + if (m->invalid) + fail (l, program, "invalid DT_AARCH64_MEMTAG_xxx dynamic tag value"); + if (m->orphan) + fail (l, program, "inconsistent DT_AARCH64_MEMTAG_xxx dynamic tags"); + if (m->heap) + fail (l, program, "MTE protection heap is not supported"); + if (m->globals) + fail (l, program, "MTE protection globals is not supported"); +} +#endif /* USE_AARCH64_MEMTAG_ABI */ + +void +_dl_mte_check (struct link_map *l, const char *program) +{ +#ifdef USE_AARCH64_MEMTAG_ABI + /* During startup L is the main executable map, otherwise it is the + dlopen'ed object. */ + bool startup = program != NULL; + + /* Whether the MTE stack protection was enabled at startup. */ + bool mte_stack = (GL(dl_aarch64_mte) & MTE_STACK) != 0; + + struct memtag_marking m = read_marking (l); + check_marking (l, program, &m); + + /* Only the main executable marking enables the MTE stack protection. If + the main executable is not marked, any dependency that requires it makes + the process to fail (either at startup or on dlopen). */ + if (startup) + mte_stack = m.stack; + else if (m.stack && !mte_stack) + fail (l, program, + "shared object requires MTE stack protection, but it was not " + "enabled at program startup"); + + for (unsigned int i = 0; i < l->l_searchlist.r_nlist; i++) + { + const struct link_map *dep = l->l_searchlist.r_list[i]; + if (dep == l) + continue; + struct memtag_marking dm = read_marking (dep); + check_marking (dep, program, &dm); + if (dm.stack && !mte_stack) + fail (dep, program, + startup + ? "shared object requires MTE stack protection, but it is not " + "enabled by the main executable" + : "shared object requires MTE stack protection, but it was not " + "enabled at program startup"); + } + + if (startup && mte_stack) + { + GL(dl_aarch64_mte) |= MTE_STACK; + /* Use asynchronous mode if DT_AARCH64_MEMTAG_MODE is not present. */ + GL(dl_aarch64_mte) |= (m.mode_present && m.mode == 0) + ? MTE_MODE_SYNC : MTE_MODE_ASYNC; + } +#endif +} + +/* Enable the MTE state required by the DT_AARCH64_MEMTAG_xxx dynamic tags of + the main executable, parsed by _dl_mte_check. It is done after initial + relocation processing, but prior any user code execution (including ELF + constructors). */ +void +_dl_mte_init (void) +{ +#ifdef USE_AARCH64_MEMTAG_ABI + if ((GL(dl_aarch64_mte) & MTE_STACK) == 0) + return; + + if ((GLRO (dl_hwcap2) & HWCAP2_MTE) == 0) + _dl_fatal_printf ("Fatal glibc error: MTE stack protection required, " + "but not supported by the kernel or CPU\n"); + + uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS; + flags |= (GL(dl_aarch64_mte) & MTE_MODE_SYNC) != 0 + ? PR_MTE_TCF_SYNC : PR_MTE_TCF_ASYNC; + + int r = INLINE_SYSCALL_CALL (prctl, PR_SET_TAGGED_ADDR_CTRL, flags, 0, 0, 0); + if (r < 0) + _dl_fatal_printf ("Fatal glibc error: failed to enable MTE: %d\n", -r); + + if (!_dl_mte_setup_stack ()) + _dl_fatal_printf ("Fatal glibc error: MTE stack protection setup " + "failed\n"); +#endif +} diff --git a/sysdeps/aarch64/dl-mte.h b/sysdeps/aarch64/dl-mte.h new file mode 100644 index 0000000000..3c872ffc7e --- /dev/null +++ b/sysdeps/aarch64/dl-mte.h @@ -0,0 +1,35 @@ +/* AArch64 MTE support. + Copyright (C) 2026 Free Software Foundation, Inc. + + 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/>. */ + +#ifndef _DL_MTE_H +#define _DL_MTE_H + +#ifndef PR_SET_TAGGED_ADDR_CTRL +# define PR_SET_TAGGED_ADDR_CTRL 55 +# define PR_MTE_TAG_SHIFT 3 +# define PR_TAGGED_ADDR_ENABLE (1UL << 0) +# define PR_MTE_TCF_SYNC (1UL << 1) +# define PR_MTE_TCF_ASYNC (1UL << 2) +#endif + +#ifndef USE_AARCH64_MEMTAG_ABI +static __always_inline bool _dl_mte_setup_stack (void) { return false; }; +#else +extern bool _dl_mte_setup_stack (void) attribute_hidden; +#endif + +#endif diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h index 0d2672d32d..a914615a45 100644 --- a/sysdeps/aarch64/dl-prop.h +++ b/sysdeps/aarch64/dl-prop.h @@ -27,11 +27,17 @@ extern void _dl_bti_check (struct link_map *, const char *) extern void _dl_gcs_check (struct link_map *, const char *, int) attribute_hidden; +extern void _dl_mte_check (struct link_map *, const char *) + attribute_hidden; + +extern void _dl_mte_init (void) attribute_hidden; + static inline void __attribute__ ((always_inline)) _rtld_main_check (struct link_map *m, const char *program) { _dl_bti_check (m, program); _dl_gcs_check (m, program, 0); + _dl_mte_check (m, program); } static inline void __attribute__ ((always_inline)) @@ -39,6 +45,7 @@ _dl_open_check (struct link_map *m, int dlopen_mode) { _dl_bti_check (m, NULL); _dl_gcs_check (m, NULL, dlopen_mode); + _dl_mte_check (m, NULL); } static inline int diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S index c278485cd3..336057ad04 100644 --- a/sysdeps/aarch64/dl-start.S +++ b/sysdeps/aarch64/dl-start.S @@ -65,6 +65,11 @@ ENTRY (_start) svc 0x0 cbnz w0, L(failed_gcs_lock) L(skip_gcs_enable): + /* Enable MTE if required by the Memtag ABI ELF marking. */ + bl _dl_mte_init + /* Reload GL, x16 might be clobbered by the call above. */ + adrp x16, _rtld_local + add x16, x16, :lo12:_rtld_local .globl _dl_start_user .type _dl_start_user, %function diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile index baefac15b5..42aa048446 100644 --- a/sysdeps/unix/sysv/linux/aarch64/Makefile +++ b/sysdeps/unix/sysv/linux/aarch64/Makefile @@ -19,6 +19,10 @@ tests-static += \ # tests-static tst-cpu-tunable-static-pie-TUNABLES = glibc.cpu.hwcaps=-midr,-sve,-mops endif + +sysdep-dl-routines += \ + dl-mte-stack \ + # sysdep-dl-routines endif ifeq ($(subdir),misc) @@ -375,6 +379,108 @@ tst-gcs-audit-override-ARGS = -- $(host-test-program-cmd) endif # ifeq ($(have-test-gcs),yes) +ifeq ($(aarch64-memtag-abi)$(have-test-memtag-stack),yesyes) +tests += \ + tst-memtag-abort-dep \ + tst-memtag-stack-async \ + tst-memtag-stack-dep1 \ + tst-memtag-stack-dep2 \ + tst-memtag-stack-dlopen \ + tst-memtag-stack-dlopen-dep \ + tst-memtag-stack-dlopen-enabled \ + tst-memtag-stack-sync \ + # tests + +modules-names += \ + tst-memtag-mod1 \ + tst-memtag-mod2 \ + tst-memtag-mod3 \ + # modules-names + +# Disable the stack protector for the stack tagging tests. Some GCC versions +# have a bug where the stack frame untagging at the function epilogue is done +# before the stack canary check. +memtag-stack-cflags = -march=armv8.5-a+memtag -fsanitize=memtag-stack \ + $(no-stack-protector) + +CFLAGS-tst-memtag-stack-sync.c += $(memtag-stack-cflags) +LDFLAGS-tst-memtag-stack-sync += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync +tst-memtag-stack-sync-ARGS = sync + +CFLAGS-tst-memtag-stack-async.c += $(memtag-stack-cflags) +LDFLAGS-tst-memtag-stack-async += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=async +tst-memtag-stack-async-ARGS = async + +CFLAGS-tst-memtag-mod2.c += -march=armv8.5-a+memtag -fsanitize=memtag-stack +LDFLAGS-tst-memtag-mod2.so += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync +$(objpfx)tst-memtag-mod2.so: $(libsupport) + +CFLAGS-tst-memtag-stack-dep1.c += $(memtag-stack-cflags) +LDFLAGS-tst-memtag-stack-dep1 += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync +$(objpfx)tst-memtag-stack-dep1: $(objpfx)tst-memtag-mod1.so +tst-memtag-stack-dep1-ARGS = sync + +CFLAGS-tst-memtag-stack-dep2.c += $(memtag-stack-cflags) +LDFLAGS-tst-memtag-stack-dep2 += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=sync \ + -Wl,--no-as-needed +$(objpfx)tst-memtag-stack-dep2: $(objpfx)tst-memtag-mod2.so +tst-memtag-stack-dep2-ARGS = sync + +$(objpfx)tst-memtag-stack-dlopen.out: $(objpfx)tst-memtag-mod2.so + +LDFLAGS-tst-memtag-mod3.so += -Wl,--no-as-needed +$(objpfx)tst-memtag-mod3.so: $(objpfx)tst-memtag-mod2.so +$(objpfx)tst-memtag-stack-dlopen-dep.out: $(objpfx)tst-memtag-mod3.so \ + $(objpfx)tst-memtag-mod1.so + +CFLAGS-tst-memtag-stack-dlopen-enabled.c += $(memtag-stack-cflags) +LDFLAGS-tst-memtag-stack-dlopen-enabled += -Wl,-z,memtag-stack \ + -Wl,-z,memtag-mode=sync +$(objpfx)tst-memtag-stack-dlopen-enabled.out: $(objpfx)tst-memtag-mod2.so + +LDFLAGS-tst-memtag-abort-dep += -Wl,--no-as-needed +$(objpfx)tst-memtag-abort-dep: $(objpfx)tst-memtag-mod2.so +tst-memtag-abort-dep-ERROR = requires MTE stack protection + +ifeq ($(have-test-memtag-heap),yes) +tests += \ + tst-memtag-abort-heap \ + # tests + +LDFLAGS-tst-memtag-abort-heap += -Wl,-z,memtag-heap -Wl,-z,memtag-mode=sync +tst-memtag-abort-heap-ERROR = MTE protection heap is not supported +endif # ifeq ($(have-test-memtag-heap),yes) + +ifeq ($(have-test-memtag-mode-only),yes) +tests += \ + tst-memtag-abort-orphan \ + tst-memtag-dlopen-orphan \ + # tests + +modules-names += \ + tst-memtag-mod4 \ + # modules-names + +LDFLAGS-tst-memtag-abort-orphan += -Wl,-z,memtag-mode=sync +tst-memtag-abort-orphan-ERROR = inconsistent DT_AARCH64_MEMTAG_xxx dynamic tags + +LDFLAGS-tst-memtag-mod4.so += -Wl,-z,memtag-mode=sync +$(objpfx)tst-memtag-dlopen-orphan.out: $(objpfx)tst-memtag-mod4.so \ + $(objpfx)tst-memtag-mod1.so +endif # ifeq ($(have-test-memtag-mode-only),yes) + +define run-memtag-abort-test + $(test-wrapper-env) $(run-program-env) $(host-test-program-cmd) +endef + +$(objpfx)tst-memtag-abort-%.out: \ + $(..)sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort.sh \ + $(objpfx)tst-memtag-abort-% + $(SHELL) $< $(common-objpfx) $(test-name) \ + '$(tst-memtag-abort-$*-ERROR)' '$(run-memtag-abort-test)'; \ + $(evaluate-test) +endif # ifeq ($(aarch64-memtag-abi)$(have-test-memtag-stack),yesyes) + endif # ifeq ($(subdir),misc) ifeq ($(subdir),stdlib) diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c b/sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c new file mode 100644 index 0000000000..5564c25cfe --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c @@ -0,0 +1,31 @@ +/* Memory tagging handling for GNU dynamic linker. AArch64 version. + 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/>. */ + +#ifdef USE_AARCH64_MEMTAG_ABI +#include <ldsodefs.h> +#include <dl-prop.h> + +bool +_dl_mte_setup_stack (void) +{ + GL(dl_stack_prot_flags) |= PROT_MTE; + void *page = PTR_ALIGN_DOWN (__libc_stack_end, GLRO (dl_pagesize)); + return __mprotect (page, GLRO (dl_pagesize), + GL(dl_stack_prot_flags) | PROT_GROWSDOWN) == 0; +} +#endif diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c index 1f3b58d0fc..f7f210c699 100644 --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c @@ -34,4 +34,18 @@ PROCINFO_CLASS unsigned long _dl_aarch64_gcs # else , # endif + +# if !defined PROCINFO_DECL && defined SHARED + ._dl_aarch64_mte +# else +PROCINFO_CLASS unsigned long _dl_aarch64_mte +# endif +# ifndef PROCINFO_DECL += 0 +# endif +# if !defined SHARED || defined PROCINFO_DECL +; +# else +, +# endif #endif diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-dep.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-dep.c new file mode 100644 index 0000000000..8459253678 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-dep.c @@ -0,0 +1,32 @@ +/* Check that an unmarked main executable with a dependency that requires + MTE stack protection aborts at startup. + 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 <support/check.h> + +void run_mte_test (void *); + +static int +do_test (void) +{ + void (*fp) (void *) = run_mte_test; + FAIL_EXIT1 ("dynamic loader did not abort with a MTE marked dependency " + "(%p)", fp); +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-heap.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-heap.c new file mode 100644 index 0000000000..f0486442b9 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-heap.c @@ -0,0 +1,28 @@ +/* Check that a main executable marked with DT_AARCH64_MEMTAG_HEAP aborts + at startup, since only the MTE stack protection is supported. + 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 <support/check.h> + +static int +do_test (void) +{ + FAIL_EXIT1 ("dynamic loader did not abort with a MTE heap marking"); +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-orphan.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-orphan.c new file mode 100644 index 0000000000..f7c08fb805 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort-orphan.c @@ -0,0 +1,29 @@ +/* Check that a main executable with an orphan DT_AARCH64_MEMTAG_MODE + dynamic tag (without any MTE protection being requested) aborts at + startup. + 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 <support/check.h> + +static int +do_test (void) +{ + FAIL_EXIT1 ("dynamic loader did not abort with an orphan MTE marking"); +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort.sh b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort.sh new file mode 100644 index 0000000000..8508730415 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-abort.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Test wrapper for AArch64 Memtag ABI tests that are expected to abort +# at startup. +# 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/>. + +objpfx=$1; shift +tstname=$1; shift +expected=$1; shift +tstrun=$1; shift + +logfile=$objpfx/$tstname.out + +rm -f $logfile +touch $logfile + +${tstrun} 2>> $logfile >> $logfile; status=$? + +if test $status -eq 127 && grep -q "$expected" "$logfile" ; then + exit 0 +elif test $status -eq 77; then + exit 77 +else + echo "expected '$expected' with 127, not $status return status" + exit 1 +fi diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-dlopen-orphan.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-dlopen-orphan.c new file mode 100644 index 0000000000..4ec4414672 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-dlopen-orphan.c @@ -0,0 +1,47 @@ +/* Check that dlopen of a module with an orphan DT_AARCH64_MEMTAG_MODE + dynamic tag fails instead of aborting the process. + 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 <dlfcn.h> +#include <string.h> +#include <support/check.h> +#include <support/xdlfcn.h> + +#include "tst-mte-helper.h" + +static int +do_test (void) +{ + TEST_VERIFY_EXIT (!mte_enable ()); + + /* The module marking is inconsistent (DT_AARCH64_MEMTAG_MODE without any + MTE protection request). The dlopen should fail without aborting the + process. */ + void *h = dlopen ("tst-memtag-mod4.so", RTLD_NOW); + TEST_VERIFY (h == NULL); + const char *message = dlerror (); + if (strstr (message, "inconsistent DT_AARCH64_MEMTAG_xxx") == NULL) + FAIL_EXIT1 ("invalid dlopen error message: %s", message); + + /* The process should still work, including loading unmarked modules. */ + xdlclose (xdlopen ("tst-memtag-mod1.so", RTLD_NOW)); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod1.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod1.c new file mode 100644 index 0000000000..07f2a2d7ba --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod1.c @@ -0,0 +1 @@ +int foo (void) { return 0; } diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod2.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod2.c new file mode 100644 index 0000000000..36d3f4f2e1 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod2.c @@ -0,0 +1 @@ +#include "tst-memtag-test-skeleton.c" diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod3.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod3.c new file mode 100644 index 0000000000..a89337d3b9 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod3.c @@ -0,0 +1,10 @@ +/* Unmarked module with a dependency (tst-memtag-mod2.so) that requires + MTE stack protection. */ + +extern void run_mte_test (void *); + +void +mod3_func (void *closure) +{ + run_mte_test (closure); +} diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod4.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod4.c new file mode 100644 index 0000000000..fdea30df4a --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod4.c @@ -0,0 +1,8 @@ +/* Module linked with an orphan DT_AARCH64_MEMTAG_MODE dynamic tag (without + any MTE protection request). */ + +int +mod4_func (void) +{ + return 0; +} diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-async.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-async.c new file mode 100644 index 0000000000..e68fcccab5 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-async.c @@ -0,0 +1,2 @@ +/* Check DT_AARCH64_MEMTAG_STACK with DT_AARCH64_MEMTAG_MODE equal 0. */ +#include "tst-memtag-stack-skeleton.c" diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep1.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep1.c new file mode 100644 index 0000000000..65c5bb6591 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep1.c @@ -0,0 +1,3 @@ +/* Check if dependencies without DT_AARCH64_MEMTAG_STACK still enables MTE + support. */ +#include "tst-memtag-stack-skeleton.c" diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep2.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep2.c new file mode 100644 index 0000000000..29c3bb48bf --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep2.c @@ -0,0 +1,3 @@ +/* Check that a marked main executable with a dependency that also requires + MTE stack protection enables the MTE stack protection. */ +#include "tst-memtag-stack-skeleton.c" diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen-dep.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen-dep.c new file mode 100644 index 0000000000..803abf000e --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen-dep.c @@ -0,0 +1,45 @@ +/* Check that without MTE stack protection enabled at startup. + 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 <dlfcn.h> +#include <string.h> +#include <support/check.h> +#include <support/xdlfcn.h> + +#include "tst-mte-helper.h" + +static int +do_test (void) +{ + TEST_VERIFY_EXIT (!mte_enable ()); + + /* The module itself is unmarked, but its dependency requires MTE stack + protection. The dlopen should fail without aborting the process. */ + void *h = dlopen ("tst-memtag-mod3.so", RTLD_NOW); + TEST_VERIFY (h == NULL); + const char *message = dlerror (); + if (strstr (message, "requires MTE stack protection") == NULL) + FAIL_EXIT1 ("invalid dlopen error message: %s", message); + + /* The process should still work, including loading unmarked modules. */ + xdlclose (xdlopen ("tst-memtag-mod1.so", RTLD_NOW)); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen-enabled.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen-enabled.c new file mode 100644 index 0000000000..b5383edb85 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen-enabled.c @@ -0,0 +1,57 @@ +/* Check that a marked main executable can dlopen a module that requires + MTE stack protection. + 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 <sys/auxv.h> +#include <support/capture_subprocess.h> +#include <support/check.h> +#include <support/xdlfcn.h> + +#include "tst-mte-helper.h" + +static void (*mod_run_mte_test) (void *); + +static void +run_dlopened_test (void *closure) +{ + mod_run_mte_test (closure); +} + +static int +do_test (void) +{ + if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE)) + FAIL_UNSUPPORTED ("kernel or CPU does not support or enable MTE"); + + TEST_VERIFY_EXIT (mte_enable ()); + + void *h = xdlopen ("tst-memtag-mod2.so", RTLD_NOW); + mod_run_mte_test = xdlsym (h, "run_mte_test"); + + struct support_capture_subprocess result = + support_capture_subprocess (run_dlopened_test, (void *) TEST_MAIN); + support_capture_subprocess_check (&result, "MTE stack fault in dlopened " + "module", EXIT_MTESERR, sc_allow_none); + support_capture_subprocess_free (&result); + + xdlclose (h); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen.c new file mode 100644 index 0000000000..071efefa42 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dlopen.c @@ -0,0 +1,45 @@ +/* Tests for MEMTAG support. + 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 <dlfcn.h> +#include <string.h> +#include <sys/auxv.h> +#include <support/check.h> + +#include "tst-mte-helper.h" + +static int +do_test (void) +{ + if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE)) + FAIL_UNSUPPORTED ("kernel or CPU does not support or enable MTE"); + + TEST_VERIFY_EXIT (!mte_enable ()); + + /* Verify that if the MTE stack protection is not enabled at startup, + trying to load a DSO that requires it should fail. */ + void *h = dlopen ("tst-memtag-mod2.so", RTLD_NOW); + TEST_VERIFY (h == NULL); + const char *message = dlerror (); + if (strstr (message, "requires MTE stack protection") == NULL) + FAIL_EXIT1 ("invalid dlopen error message: %s", message); + + return 0; +} + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-skeleton.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-skeleton.c new file mode 100644 index 0000000000..229664a617 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-skeleton.c @@ -0,0 +1,72 @@ +/* Tests for MEMTAG support. + 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 <string.h> +#include <sys/auxv.h> +#include <support/capture_subprocess.h> +#include <support/check.h> + +#include "tst-memtag-test-skeleton.c" + +static int +do_test (int argc, char *argv[]) +{ + if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE)) + FAIL_UNSUPPORTED ("kernel or CPU does not support or enable MTE"); + + TEST_VERIFY_EXIT (mte_enable ()); + /* We must have + - one or four parameters left if called initially + + path for ld.so optional + + "--library-path" optional + + the library path optional + + the application name + + the expected MTE mode + */ + TEST_VERIFY_EXIT (argc == 2); + int mode = mte_mode (); + if (strcmp (argv[1], "sync") == 0) + TEST_VERIFY_EXIT (mode == PR_MTE_TCF_SYNC); + else if (strcmp (argv[1], "async") == 0) + TEST_VERIFY_EXIT (mode == PR_MTE_TCF_ASYNC); + + { + struct support_capture_subprocess result = + support_capture_subprocess (run_mte_test, (void*)TEST_MAIN); + + support_capture_subprocess_check (&result, "MTE main stack", + EXIT_MTESERR, sc_allow_none); + + support_capture_subprocess_free (&result); + } + + { + struct support_capture_subprocess result = + support_capture_subprocess (run_mte_test, (void*)TEST_THREAD); + + support_capture_subprocess_check (&result, "MTE thread stack", + EXIT_MTESERR, sc_allow_none); + + support_capture_subprocess_free (&result); + } + + return 0; +} +#define TEST_FUNCTION_ARGV do_test + +#include <support/test-driver.c> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-sync.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-sync.c new file mode 100644 index 0000000000..e68fcccab5 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-sync.c @@ -0,0 +1,2 @@ +/* Check DT_AARCH64_MEMTAG_STACK with DT_AARCH64_MEMTAG_MODE equal 0. */ +#include "tst-memtag-stack-skeleton.c" diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-memtag-test-skeleton.c b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-test-skeleton.c new file mode 100644 index 0000000000..52e4f9fc31 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-test-skeleton.c @@ -0,0 +1,85 @@ +/* Tests skeleton for MEMTAG tests. + 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 <stdlib.h> +#include <libc-diag.h> +#include <support/check.h> +#include <support/xsignal.h> +#include <support/xthread.h> +#include <unistd.h> + +#include "tst-mte-helper.h" + +static void +sigsegv_handler (int signum, siginfo_t *si, void *context) +{ + if (si->si_signo == SIGSEGV + && (si->si_code == SEGV_MTESERR || si->si_code == SEGV_MTEAERR)) + _exit (EXIT_MTESERR); + else + _exit (EXIT_FAILURE); +} + +/* Prevent inlining so the stack frame is definitively constructed to + trigger a stack frame, and optimization to avoid compiler optimize + away the invalid stack operation. */ +static void * +__attribute_noinline__ +__attribute_optimization_barrier__ +trigger_mte_fault (void *closure) +{ + DIAG_PUSH_NEEDS_COMMENT; + DIAG_IGNORE_NEEDS_COMMENT_GCC (16, "-Warray-bounds"); + + sigset_t mask; + sigemptyset (&mask); + sigaddset (&mask, SIGSEGV); + + /* Aling to a MTE tag granule. */ + _Alignas (16) char stack_buffer[16]; + volatile char *ptr = &stack_buffer[16]; + + *(volatile char *)ptr; + + return NULL; +} + +void +run_mte_test (void *closure) +{ + test_mode mode = (test_mode)(uintptr_t)closure; + + { + struct sigaction sa = { + .sa_sigaction = sigsegv_handler, + .sa_flags = SA_NODEFER | SA_SIGINFO, + }; + sigemptyset (&sa.sa_mask); + xsigaction (SIGSEGV, &sa, NULL); + } + + if (mode == TEST_MAIN) + trigger_mte_fault (NULL); + else + { + pthread_t t = xpthread_create (NULL, trigger_mte_fault, NULL); + xpthread_join (t); + } + + _exit (EXIT_FAILURE); +} diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h b/sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h new file mode 100644 index 0000000000..bcf324a36f --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h @@ -0,0 +1,57 @@ +/* Helper routines to check MEMTAG support. + 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/>. */ + +#ifndef TST_MTE_HELPER_H +#define TST_MTE_HELPER_H + +#include <fcntl.h> +#include <limits.h> +#include <string.h> +#include <stdbool.h> +#include <sys/prctl.h> +#include <stdio.h> + +typedef enum +{ + TEST_MAIN = 0, + TEST_THREAD = 1, +} test_mode; + +#define EXIT_MTESERR 79 + +void run_mte_test (void *); + +static inline bool +mte_enable (void) +{ + int ctrl = prctl (PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); + return ctrl > 0 && (ctrl & PR_MTE_TCF_MASK) != PR_MTE_TCF_NONE; +} + +static inline int +mte_mode (void) +{ + int ctrl = prctl (PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0); + TEST_VERIFY_EXIT (ctrl >= 0); + return ctrl & PR_MTE_TCF_MASK; +} + +#define __attribute_disable_mte_stack__ \ + __attribute__((no_sanitize("memtag-stack"))) + +#endif