[glibc/azanella/aarch64-memtag] aarch64: Add initial Memtag ABI Extension to ELF
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=a8bcb6c0615dc3cdae238c2b52cc697c51eef2db commit a8bcb6c0615dc3cdae238c2b52cc697c51eef2db Author: Adhemerval Zanella <[email protected]> Date: Tue Mar 10 16:11:06 2026 +0200 aarch64: Add initial Memtag ABI Extension to ELF This patch introduces initial support for the AArch64 Memory Tagging Extension (MTE) in the dynamic linker, specifically 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]. This feature is guarded by a new configure option, --enable-aarch64-memtag-abi, and it is disable by default. Key additions and behaviors include: * ELF Tag Parsing & Validation: the dynamic linker now parses the new AArch64-specific dynamic tags. Currently, only stack tagging is supported, and if an executable or its dependencies request heap tagging (DT_AARCH64_MEMTAG_HEAP) or global tagging (DT_AARCH64_MEMTAG_GLOBALS), the dynamic linker will emit an unsupported error and fail safely. * MTE Mode Resolution: the execution mode (synchronous or asynchronous) is determined by DT_AARCH64_MEMTAG_MODE. If absent, the system defaults to asynchronous mode. * Interaction with --enable-memory-tagging: if the glibc build has --enable-memory-tagging (USE_MTAG) configured, the standard glibc.mem.tagging tunable takes precedence. The tunable can override the MTE mode requested by the ELF ABI (e.g., forcing synchronous mode or deferring to system-preferred auto mode) and can independently enable heap tagging for the libc allocator. * Late Loading constraints: if the main executable does not enable the MTE stack at startup, any subsequent attempts to dlopen a DSO that requires MTE stack support will be rejected to prevent unsafe memory access. A comprehensive set of tests has been added to verify the ABI: * tst-memtag-stack-{sync,async}: verifies that compiling with -fsanitize=memtag-stack and linking with -z,memtag-mode=... correctly sets up the respective execution environment. * tst-memtag-stack-{dep1,dep2}: ensures that dependencies correctly inherit or trigger the MTE setup during the loading phase. * tst-memtag-stack-dlopen: verifies the dlopen constraints, ensuring that an MTE-dependent module fails to load with an appropriate dlerror if MTE was not initialized at program startup. * tst-memtag-stack-sync-tunable: check if the can override the dynamic tag value. * Fault handling is verified across main and spawned threads by intentionally triggering MTE boundary violations and asserting receipt of SIGSEGV with SEGV_MTESERR/SEGV_MTEAERR. [1] https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst Diff: --- INSTALL | 14 ++ NEWS | 5 + config.h.in | 3 + config.make.in | 1 + configure | 24 +++ configure.ac | 14 ++ elf/Versions | 7 + elf/elf.h | 7 +- malloc/arena.c | 2 +- manual/install.texi | 21 ++ sysdeps/aarch64/Makefile | 1 + sysdeps/aarch64/configure | 103 ++++++++++ sysdeps/aarch64/configure.ac | 25 +++ sysdeps/aarch64/cpu-features.h | 14 +- sysdeps/aarch64/dl-diagnostics-cpu.c | 2 - sysdeps/aarch64/dl-mte.c | 227 +++++++++++++++++++++ sysdeps/aarch64/dl-mte.h | 35 ++++ sysdeps/aarch64/dl-prop.h | 7 + sysdeps/aarch64/dl-start.S | 2 + sysdeps/aarch64/libc-mtag.h | 6 + sysdeps/generic/libc-mtag.h | 6 + sysdeps/unix/sysv/linux/aarch64/Makefile | 57 ++++++ sysdeps/unix/sysv/linux/aarch64/cpu-features.c | 27 --- sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c | 31 +++ sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c | 14 ++ sysdeps/unix/sysv/linux/aarch64/libc-start.h | 2 + sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod1.c | 1 + sysdeps/unix/sysv/linux/aarch64/tst-memtag-mod2.c | 1 + .../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 | 59 ++++++ .../sysv/linux/aarch64/tst-memtag-stack-dlopen.c | 45 ++++ .../sysv/linux/aarch64/tst-memtag-stack-skeleton.c | 74 +++++++ .../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 ++++++ 36 files changed, 952 insertions(+), 34 deletions(-) diff --git a/INSTALL b/INSTALL index 4174c9661f..2c264c0b96 100644 --- a/INSTALL +++ b/INSTALL @@ -299,6 +299,20 @@ 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. + + Unsupported options (DT_AARCH64_MEMTAG_HEAP and DT_AARCH64_MEMTAG_GLOBALS) + triggers either startup failure or dlopen errors. + + If `--enable-memory-tagging` is also uses, the `glibc.mem.tagging` tunable + takes precedence. + + This option is disabled by default and only support 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 c6e9a83923..ea9fc1af1f 100644 --- a/NEWS +++ b/NEWS @@ -16,6 +16,11 @@ Major new features: * The SVID handling for cosh and sinh were moved to compat symbols, allowing improvements in performance. +* On 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). This can be enabled using thw + 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 b53731c393..65457d7fe3 100644 --- a/config.h.in +++ b/config.h.in @@ -185,6 +185,9 @@ /* Define if memory tagging support should be enabled. */ #undef USE_MTAG +/* 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 856363d0cd..8be2096511 100644 --- a/config.make.in +++ b/config.make.in @@ -79,6 +79,7 @@ multi-arch = @multi_arch@ mach-interface-list = @mach_interface_list@ memory-tagging = @memory_tagging@ +aarch64-memtag-abi = @aarch64_memtag_abi@ # Configuration options. build-shared = @shared@ diff --git a/configure b/configure index c84d283bd5..25ebb86a40 100755 --- a/configure +++ b/configure @@ -703,6 +703,7 @@ INSTALL_PROGRAM base_machine build_pt_chown build_nscd +aarch64_memtag_abi memory_tagging enable_werror force_install @@ -812,6 +813,7 @@ enable_kernel enable_werror enable_multi_arch enable_memory_tagging +enable_aarch64_memtag_abi enable_systemtap enable_build_nscd enable_nscd @@ -1492,6 +1494,9 @@ Optional Features: architectures --enable-memory-tagging enable memory tagging if supported by the architecture [default=no] + --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 @@ -4710,6 +4715,25 @@ if test "$memory_tagging" = yes; then 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 280ec8e5b3..a2275af4ec 100644 --- a/configure.ac +++ b/configure.ac @@ -343,6 +343,20 @@ if test "$memory_tagging" = yes; then fi AC_SUBST(memory_tagging) +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 46a01281cb..02c4125cb1 100644 --- a/elf/elf.h +++ b/elf/elf.h @@ -3039,7 +3039,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/malloc/arena.c b/malloc/arena.c index ddde32c712..8732721b03 100644 --- a/malloc/arena.c +++ b/malloc/arena.c @@ -253,7 +253,7 @@ __ptmalloc_init (void) #endif #ifdef USE_MTAG - if ((TUNABLE_GET_FULL (glibc, mem, tagging, int32_t, NULL) & 1) != 0) + if (__libc_mtag_enabled ()) { /* If the tunable says that we should be using tagged memory and that morecore does not support tagged regions, then diff --git a/manual/install.texi b/manual/install.texi index 073cda0530..99a0023664 100644 --- a/manual/install.texi +++ b/manual/install.texi @@ -328,6 +328,27 @@ 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 (DT_AARCH64_MEMTAG_STACK) and MTE +execution mode configuration (DT_AARCH64_MEMTAG_MODE) as defined in the +Memtag ABI Extension for ELF64. + +Unsupported options (DT_AARCH64_MEMTAG_HEAP and DT_AARCH64_MEMTAG_GLOBALS) +triggers either startup failure or dlopen errors. If the execution mode +is not specified, the system defaults to asynchronous mode. + +If @option{--enable-memory-tagging} is also uses, the @code{glibc.mem.tagging} +tunable takes precedence. The tunable can override the MTE mode requested by +the MEMTAG ABI (e.g., forcing synchronous mode or deferring to the +system-preferred auto mode) and can independently enable heap tagging. + +If the main executable does not enable the MTE stack at startup, any +subsequent attempts to @code{dlopen} a shared object that requires MTE +support will be rejected. + +This option is disabled by default and only support 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 d6c5cc96ca..dfbc33d2f4 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..2e1a4c62ca 100755 --- a/sysdeps/aarch64/configure +++ b/sysdeps/aarch64/configure @@ -484,3 +484,106 @@ else have-test-gcs = no" fi +# Check if compiler supports MEMTAG stack support. Also check if linker +# support the required options (clang might only support ti for Android +# builds). + +{ 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 + +{ 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,-z,memtag-stack -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,-z,memtag-stack -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 + diff --git a/sysdeps/aarch64/configure.ac b/sysdeps/aarch64/configure.ac index b9981861f6..210c37ca54 100644 --- a/sysdeps/aarch64/configure.ac +++ b/sysdeps/aarch64/configure.ac @@ -105,3 +105,28 @@ 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 (clang might only support ti for Android +# builds). +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] +) +LIBC_LINKER_FEATURE( + [-z memtag-stack], [-Wl,-z,memtag-stack], + [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 diff --git a/sysdeps/aarch64/cpu-features.h b/sysdeps/aarch64/cpu-features.h index d6367a4596..005a19818c 100644 --- a/sysdeps/aarch64/cpu-features.h +++ b/sysdeps/aarch64/cpu-features.h @@ -59,15 +59,23 @@ enum { BTI_CHECK_ENFORCED = 1, }; +enum { + MTE_MODE_SYNC = 0x1, + MTE_MODE_ASYNC = 0x2, + MTE_MODE_AUTO = 0x4, + MTE_MODE_MASK = MTE_MODE_SYNC | MTE_MODE_ASYNC | MTE_MODE_AUTO, + MTE_STACK = 0x8, + MTE_HEAP = 0x10, +}; + struct cpu_features { uint64_t midr_el1; unsigned zva_size; bool bti; - /* Currently, the GLIBC memory tagging tunable only defines 8 bits. */ - uint8_t mte_state; + uint8_t unused0; bool sve; - bool unused; + bool unused1; bool mops; }; diff --git a/sysdeps/aarch64/dl-diagnostics-cpu.c b/sysdeps/aarch64/dl-diagnostics-cpu.c index 697868cb25..4bf244ec71 100644 --- a/sysdeps/aarch64/dl-diagnostics-cpu.c +++ b/sysdeps/aarch64/dl-diagnostics-cpu.c @@ -45,8 +45,6 @@ _dl_diagnostics_cpu (void) print_cpu_features_value ("midr_el1", GLRO (dl_aarch64_cpu_features).midr_el1); print_cpu_features_value ("mops", GLRO (dl_aarch64_cpu_features).mops); - print_cpu_features_value ("mte_state", - GLRO (dl_aarch64_cpu_features).mte_state); print_cpu_features_value ("sve", GLRO (dl_aarch64_cpu_features).sve); print_cpu_features_value ("zva_size", GLRO (dl_aarch64_cpu_features).zva_size); diff --git a/sysdeps/aarch64/dl-mte.c b/sysdeps/aarch64/dl-mte.c new file mode 100644 index 0000000000..2447bfa2d6 --- /dev/null +++ b/sysdeps/aarch64/dl-mte.c @@ -0,0 +1,227 @@ +/* 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/>. */ + +#include <assert.h> +#include <ldsodefs.h> +#include <sys/auxv.h> +#include <dl-tunables.h> +#include <dl-mte.h> +#include <dl-prop.h> + +#pragma GCC optimize ("O0") + +/* The maximal set of permitted tags that the MTE random tag generation + instruction may use. We exclude tag 0 because a) we want to reserve + that for the libc heap structures and b) because it makes it easier + to see when pointer have been correctly tagged. */ +#define MTE_ALLOWED_TAGS (0xfffe << PR_MTE_TAG_SHIFT) + +#ifdef USE_AARCH64_MEMTAG_ABI + +# define DT_AARCH64(x) (DT_AARCH64_##x - DT_LOPROC + DT_NUM) + +static void +fail (const struct link_map *l, const char *program, const char *mode) +{ + if (program != NULL) + { + if (program[0] != '\0' && l->l_name[0] != '\0') + _dl_fatal_printf ("%s: %s: MTE protection %s is not supported\n", + program, l->l_name, mode); + if (program[0] != '\0') + _dl_fatal_printf ("%s: MTE protection %s is not supported\n", + program, mode); + _dl_fatal_printf ("error: MTE protection %s is not supported\n", + mode); + } + else + _dl_signal_error (0, l->l_name, "dlopen", "MTE is not enabled"); +} + +static void +fail_mode (const struct link_map *l, const char *program, uint64_t mode) +{ + assert (program != NULL); + if (program[0] != '\0' && l->l_name[0] != '\0') + _dl_fatal_printf ("%s: %s: MTE mode 0x%lx not supported\n", + program, l->l_name, mode); + if (program[0] != '\0') + _dl_fatal_printf ("%s: MTE mode 0x%lx not supported\n", program, mode); + _dl_fatal_printf ("error: MTE mode 0x%lx not supported\n", mode); +} + +static void +unsupported_stack (void) +{ + _dl_fatal_printf ("error: MTE stack required, but kernel does not support MTE\n"); +} + +static bool requires_mte_heap (const struct link_map *l) +{ + return l->l_info[DT_AARCH64 (MEMTAG_HEAP)] != NULL; +} + +static bool requires_mte_stack (const struct link_map *l) +{ + return l->l_info[DT_AARCH64 (MEMTAG_STACK)] != NULL; +} + +static bool requires_mte_globals (const struct link_map *l) +{ + return l->l_info[DT_AARCH64 (MEMTAG_GLOBALS)] != NULL + || l->l_info[DT_AARCH64 (MEMTAG_GLOBALSSZ)] != NULL; +} + +static void check_mte_mode (const struct link_map *l, const char *program) +{ + /* Only the executable are considered for the MTE mode. */ + const ElfW(Dyn) *d = l->l_info[DT_AARCH64 (MEMTAG_MODE)]; + if (d != NULL && l->l_type == lt_executable) + { + if (d->d_un.d_val == 0) + GL(dl_aarch64_mte) |= MTE_MODE_SYNC; + else if (d->d_un.d_val == 1) + GL(dl_aarch64_mte) |= MTE_MODE_ASYNC; + else + fail_mode (l, program, d->d_un.d_val); + } + else + /* Use sync by default or if the dynamic tag is not present. */ + GL(dl_aarch64_mte) |= MTE_MODE_ASYNC; +} + +static void check_mte (const struct link_map *l, const char *program) +{ + if (requires_mte_stack (l)) + GL(dl_aarch64_mte) |= MTE_STACK; + if (requires_mte_heap (l)) + fail (l, program, "heap"); + if (requires_mte_globals (l)) + fail (l, program, "globals"); +} +#endif + +static inline uint8_t mte_mode (void) +{ + return GL(dl_aarch64_mte) & MTE_MODE_MASK; +} + +static inline bool enable_mte_stack (void) +{ +#ifdef USE_AARCH64_MEMTAG_ABI + return GL(dl_aarch64_mte) & MTE_STACK; +#else + return false; +#endif +} + +static inline bool enable_mte_heap (void) +{ +#ifdef USE_MTAB + return GL(dl_aarch64_mte) & MTE_HEAD; +#else + return false; +#endif +} + +static inline bool enable_mte (void) +{ + return enable_mte_stack () || enable_mte_heap (); +} + +void +_dl_mte_check (struct link_map *l, const char *program) +{ +#ifdef USE_AARCH64_MEMTAG_ABI + check_mte_mode (l, program); + + check_mte (l, program); + for (unsigned int i = 0; i < l->l_searchlist.r_nlist; i++) + check_mte (l->l_searchlist.r_list[i], program); + + /* For dlopen, if program has not enabled MTE stack at the startup, signal + that the module can not be loaded. */ + if (enable_mte_stack () && program == NULL) + { + GL(dl_aarch64_mte) &= ~MTE_STACK; + + fail (l, program, "stack"); + } +#endif +} + +void +_dl_mte_init (void) +{ +#if defined USE_MTAG || defined USE_AARCH64_MEMTAG_ABI + if ((GLRO (dl_hwcap2) & HWCAP2_MTE) == 0 || !enable_mte ()) + { +# ifdef USE_AARCH64_MEMTAG_ABI + if (GL(dl_aarch64_mte) & MTE_STACK) + unsupported_stack (); +# endif + GL(dl_aarch64_mte) = 0; + return; + } + +#ifdef USE_MTAG + /* The tunable can override the MTE mode from the MEMTAG ABI. */ + if (TUNABLE_IS_INITIALIZED_FULL (glibc, mem, tagging)) + { + int32_t mte_tunable = TUNABLE_GET_FULL (glibc, mem, tagging, int32_t, + NULL); + if (mte_tunable & 0x1) + GL(dl_aarch64_mte) |= MTE_HEAP; + if (mte_tunable & 0x2) + { + GL(dl_aarch64_mte) &= ~MTE_MODE_ASYNC; + GL(dl_aarch64_mte) |= MTE_MODE_SYNC; + } + if (mte_tunable & 0x4) + { + /* The documentatation states that it is either precise or deferred + faulting mode, so let the kernel handle it. */ + GL(dl_aarch64_mte) &= ~(MTE_MODE_ASYNC | MTE_MODE_SYNC); + GL(dl_aarch64_mte) |= MTE_MODE_AUTO; + } + } +#endif + + uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS; + switch (mte_mode ()) + { + case MTE_MODE_SYNC: + flags |= PR_MTE_TCF_SYNC; + break; + case MTE_MODE_AUTO: + flags |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC; + break; + case MTE_MODE_ASYNC: + flags |= PR_MTE_TCF_ASYNC; + break; + } + + int r = INLINE_SYSCALL_CALL (prctl, PR_SET_TAGGED_ADDR_CTRL, flags, 0, 0, 0); + if (r == -1) + _dl_fatal_printf ("failed to enable MTE: %d\n", -r); +#endif + +#ifdef USE_AARCH64_MEMTAG_ABI + if (enable_mte_stack() && !_dl_mte_setup_stack ()) + _dl_fatal_printf ("error: MTE stack 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 cf236df59b..7969260ba2 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 void __attribute__ ((always_inline)) diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S index c278485cd3..a9a66fc881 100644 --- a/sysdeps/aarch64/dl-start.S +++ b/sysdeps/aarch64/dl-start.S @@ -65,6 +65,8 @@ ENTRY (_start) svc 0x0 cbnz w0, L(failed_gcs_lock) L(skip_gcs_enable): + /* Enable MTE support */ + bl _dl_mte_init .globl _dl_start_user .type _dl_start_user, %function diff --git a/sysdeps/aarch64/libc-mtag.h b/sysdeps/aarch64/libc-mtag.h index 1d7368b806..c1cf66d7b4 100644 --- a/sysdeps/aarch64/libc-mtag.h +++ b/sysdeps/aarch64/libc-mtag.h @@ -64,6 +64,12 @@ __libc_mtag_new_tag (void *p) return x0; } +static __always_inline bool +__libc_mtag_enabled (void) +{ + return GL(dl_aarch64_mte) & MTE_HEAP; +} + #endif /* USE_MTAG */ #endif /* _AARCH64_LIBC_MTAG_H */ diff --git a/sysdeps/generic/libc-mtag.h b/sysdeps/generic/libc-mtag.h index 5477bfa17f..05e0efdce6 100644 --- a/sysdeps/generic/libc-mtag.h +++ b/sysdeps/generic/libc-mtag.h @@ -70,4 +70,10 @@ __libc_mtag_new_tag (void *p) return p; } +static __always_inline bool +__libc_mtag_enabled (void) +{ + return false; +} + #endif /* _GENERIC_LIBC_MTAG_H */ diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile index 57461fded7..fe776c52e2 100644 --- a/sysdeps/unix/sysv/linux/aarch64/Makefile +++ b/sysdeps/unix/sysv/linux/aarch64/Makefile @@ -9,6 +9,10 @@ modules-names += \ LDFLAGS-tst-tlsdesc-pac = -rdynamic $(objpfx)tst-tlsdesc-pac.out: $(objpfx)tst-tlsdesc-pac-mod.so + +sysdep-dl-routines += \ + dl-mte-stack \ + # sysdep-dl-routines endif ifeq ($(subdir),misc) @@ -364,6 +368,59 @@ 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-stack-async \ + tst-memtag-stack-dep1 \ + tst-memtag-stack-dep2 \ + tst-memtag-stack-dlopen \ + tst-memtag-stack-sync \ + # tests + +tests-special += \ + $(objpfx)tst-memtag-stack-sync-tunable.out \ + # tests-special + +modules-names += \ + tst-memtag-mod1 \ + tst-memtag-mod2 \ + # modules-names + +# Disable stack protector for stack tagging tests. Some GCC versions have a +# a bug where the stack frame untagging at the function epiloge is done before +# stack canary check. +CFLAGS-tst-memtag-stack-sync.c += -march=armv8.5-a+memtag -fsanitize=memtag-stack \ + $(no-stack-protector) +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 += -march=armv8.5-a+memtag -fsanitize=memtag-stack \ + -DMEMTAG_MODE_ASYNC $(no-stack-protector) +LDFLAGS-tst-memtag-stack-async += -Wl,-z,memtag-stack -Wl,-z,memtag-mode=async +tst-memtag-stack-async-ARGS = async + +CFLAGS-tst-memtag-stack-dep1.c += -march=armv8.5-a+memtag -fsanitize=memtag-stack \ + $(no-stack-protector) +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 + +$(objpfx)tst-memtag-stack-dep2: $(objpfx)tst-memtag-mod2.so +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) + +$(objpfx)tst-memtag-stack-dlopen.out: $(objpfx)tst-memtag-mod2.so + +$(objpfx)tst-memtag-stack-sync-tunable.out: $(objpfx)tst-memtag-stack-async + $(test-program-cmd-before-env) \ + $(run-program-env) \ + GLIBC_TUNABLES=glibc.mem.tagging=2 \ + $(test-program-cmd-after-env) sync $< \ + > $@ 2>&1; echo "status: $$?" >> $@; \ + $(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/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c index 36bd72bb12..2ee60d5520 100644 --- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c @@ -96,33 +96,6 @@ init_cpu_features (struct cpu_features *cpu_features) if (cpu_features->bti) GLRO (dl_aarch64_bti) = TUNABLE_GET (glibc, cpu, aarch64_bti, uint64_t, 0); - /* Setup memory tagging support if the HW and kernel support it, and if - the user has requested it. */ - cpu_features->mte_state = 0; - -#ifdef USE_MTAG - int mte_state = TUNABLE_GET (glibc, mem, tagging, unsigned, 0); - cpu_features->mte_state = (GLRO (dl_hwcap2) & HWCAP2_MTE) ? mte_state : 0; - /* If we lack the MTE feature, disable the tunable, since it will - otherwise cause instructions that won't run on this CPU to be used. */ - TUNABLE_SET (glibc, mem, tagging, cpu_features->mte_state); - - if (cpu_features->mte_state & 4) - /* Enable choosing system-preferred faulting mode. */ - __prctl (PR_SET_TAGGED_ADDR_CTRL, - (PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC - | MTE_ALLOWED_TAGS), - 0, 0, 0); - else if (cpu_features->mte_state & 2) - __prctl (PR_SET_TAGGED_ADDR_CTRL, - (PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | MTE_ALLOWED_TAGS), - 0, 0, 0); - else if (cpu_features->mte_state) - __prctl (PR_SET_TAGGED_ADDR_CTRL, - (PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC | MTE_ALLOWED_TAGS), - 0, 0, 0); -#endif - /* Check if SVE is supported. */ cpu_features->sve = GLRO (dl_hwcap) & HWCAP_SVE; 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/libc-start.h b/sysdeps/unix/sysv/linux/aarch64/libc-start.h index 4ccd13741b..1c51482374 100644 --- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h +++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h @@ -72,6 +72,8 @@ aarch64_libc_setup_tls (void) _dl_fatal_printf ("failed to lock GCS: %d\n", -ret); } } + + _dl_mte_init (); } # define ARCH_SETUP_IREL() apply_irel () 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-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..e383a83328 --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-dep2.c @@ -0,0 +1,59 @@ +/* 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 <sys/auxv.h> +#include <support/capture_subprocess.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"); + + /* If any shared library requires MTE stack support, but the main binary has + not DT_AARCH64_MEMTAG_MODE, defaults to asynchronous mode. */ + TEST_VERIFY_EXIT (mte_enable ()); + TEST_VERIFY_EXIT (mte_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; +} + +#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..ad103356e9 --- /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 MTE is not enabled at startup, trying to load a DSO that + requires MTE should fail. */ + void *h = dlopen ("tst-memtag-mod2.so", RTLD_NOW); + TEST_VERIFY (h == NULL); + const char *message = dlerror (); + if (strstr (message, "MTE is not enabled") == 0) + FAIL_EXIT1 ("invalid dlopen error 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..599074356c --- /dev/null +++ b/sysdeps/unix/sysv/linux/aarch64/tst-memtag-stack-skeleton.c @@ -0,0 +1,74 @@ +/* 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); + /* glibc.mem.tagging=0x3 ("auto") can e either sync, async, or asymm; so + there it no point of checking it. */ + + { + 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