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
This commit is contained in:
Adhemerval Zanella
2026-08-25 09:09:17 -03:00
parent 53e5f02a6b
commit 8db1bd3af5
39 changed files with 1307 additions and 1 deletions
+9
View File
@@ -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 '***'.
+5
View File
@@ -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
+3
View File
@@ -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
+2
View File
@@ -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@
Vendored
+24
View File
@@ -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 :
+14
View File
@@ -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@:>@])],
+7
View File
@@ -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;
+6 -1
View File
@@ -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
+23
View File
@@ -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
+1
View File
@@ -4,6 +4,7 @@ ifeq ($(subdir),elf)
sysdep-dl-routines += \
dl-bti \
dl-gcs \
dl-mte \
# sysdep-dl-routines
tests += \
+191
View File
@@ -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
+51
View File
@@ -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
+7
View File
@@ -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;
+199
View File
@@ -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
}
+35
View File
@@ -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
+7
View File
@@ -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
+5
View File
@@ -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
+106
View File
@@ -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)
@@ -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
@@ -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
@@ -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>
@@ -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>
@@ -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>
@@ -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
@@ -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>
@@ -0,0 +1 @@
int foo (void) { return 0; }
@@ -0,0 +1 @@
#include "tst-memtag-test-skeleton.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);
}
@@ -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;
}
@@ -0,0 +1,2 @@
/* Check DT_AARCH64_MEMTAG_STACK with DT_AARCH64_MEMTAG_MODE equal 0. */
#include "tst-memtag-stack-skeleton.c"
@@ -0,0 +1,3 @@
/* Check if dependencies without DT_AARCH64_MEMTAG_STACK still enables MTE
support. */
#include "tst-memtag-stack-skeleton.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"
@@ -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>
@@ -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>
@@ -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>
@@ -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>
@@ -0,0 +1,2 @@
/* Check DT_AARCH64_MEMTAG_STACK with DT_AARCH64_MEMTAG_MODE equal 0. */
#include "tst-memtag-stack-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);
}
@@ -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