[2/2] malloc: change tunable glibc.mem.tagging to glibc.mem.aarch64_mte
Checks
| Context |
Check |
Description |
| redhat-pt-bot/TryBot-apply_patch |
success
|
Patch applied to master at the time it was sent
|
| redhat-pt-bot/TryBot-32bit |
success
|
Build for i686
|
| linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-arm |
success
|
Test passed
|
| linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 |
success
|
Test passed
|
Commit Message
The 'glibc.mem.tagging' tunable was added as a generic tunable although
at the time memory tagging was only supported by aarch64.
The values for such a tunable are likely to be target-dependent.
This commit removes 'glibc.mem.tagging' and instead adds a new target
specific tunable 'glibc.mem.aarch64_mte' that accepts string values:
- 'none': memory tagging is disabled (the default).
- 'auto': enable CPU preferred tag checking mode.
- 'sync': enable synchronous tag check fault mode.
- 'async': enable asynchronous tag check fault mode.
This tunable affects the flags used for the PR_TAGGED_ADDR_ENABLE
prctl syscall and only has effect when the system supports HWCAP2_MTE.
This commit also changes when this prctl syscall happens. There are
three stages:
1) Check if the system supports HWCAP2_MTE and get the value of
the 'glibc.mem.aarch64_mte' tunable if it does.
2) Check ELF marking and amend 'aarch64_mte' value if necessary
(note: this commit doesn't add any actual checks here, just
the way to implement them if required).
3) Make the prctl syscall based on the 'aarch64_mte' value.
If some MTE mode was requested but the prctl call failed, it is an error.
The Glibc manual has been updated accordingly.
This commit also adds 2 new tests for memory tagging in malloc on
aarch64 and fixes a related small issue in __libc_free unblocking
most straightforward use cases. Note, however, that memtag support
in malloc remains experimental.
---
elf/dl-tunables.list | 5 -
malloc/arena.c | 5 +-
malloc/malloc.c | 2 +-
manual/tunables.texi | 36 ++----
sysdeps/aarch64/Makefile | 1 +
sysdeps/aarch64/cpu-features.h | 11 +-
sysdeps/aarch64/dl-diagnostics-cpu.c | 3 +-
sysdeps/aarch64/dl-mte.c | 65 +++++++++++
sysdeps/aarch64/dl-prop.h | 11 ++
sysdeps/aarch64/dl-start.S | 4 +
sysdeps/aarch64/dl-tunables.list | 5 +
sysdeps/aarch64/libc-mtag.h | 20 +++-
sysdeps/generic/libc-mtag.h | 15 +++
sysdeps/unix/sysv/linux/aarch64/Makefile | 17 +++
.../unix/sysv/linux/aarch64/cpu-features.c | 51 +++++----
.../unix/sysv/linux/aarch64/dl-procruntime.c | 16 +++
sysdeps/unix/sysv/linux/aarch64/libc-start.h | 9 ++
.../unix/sysv/linux/aarch64/tst-mte-helper.h | 104 ++++++++++++++++++
.../unix/sysv/linux/aarch64/tst-mte-malloc.c | 68 ++++++++++++
.../unix/sysv/linux/aarch64/tst-mte-realloc.c | 64 +++++++++++
20 files changed, 448 insertions(+), 64 deletions(-)
create mode 100644 sysdeps/aarch64/dl-mte.c
create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h
create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
Comments
On 20/03/26 08:36, Yury Khrustalev wrote:
> The 'glibc.mem.tagging' tunable was added as a generic tunable although
> at the time memory tagging was only supported by aarch64.
>
> The values for such a tunable are likely to be target-dependent.
>
> This commit removes 'glibc.mem.tagging' and instead adds a new target
> specific tunable 'glibc.mem.aarch64_mte' that accepts string values:
>
> - 'none': memory tagging is disabled (the default).
> - 'auto': enable CPU preferred tag checking mode.
> - 'sync': enable synchronous tag check fault mode.
> - 'async': enable asynchronous tag check fault mode.
>
> This tunable affects the flags used for the PR_TAGGED_ADDR_ENABLE
> prctl syscall and only has effect when the system supports HWCAP2_MTE.
>
> This commit also changes when this prctl syscall happens. There are
> three stages:
>
> 1) Check if the system supports HWCAP2_MTE and get the value of
> the 'glibc.mem.aarch64_mte' tunable if it does.
> 2) Check ELF marking and amend 'aarch64_mte' value if necessary
> (note: this commit doesn't add any actual checks here, just
> the way to implement them if required).
> 3) Make the prctl syscall based on the 'aarch64_mte' value.
>
> If some MTE mode was requested but the prctl call failed, it is an error.
>
> The Glibc manual has been updated accordingly.
This patch should be marked as RFC since it ties the MTE support to
your proposed GNU attributes [1] and it still has missing semantic
on how to enable GLRO(dl_aarch64_cpu_features).mte.
[1] https://github.com/ARM-software/abi-aa/issues/382
>
> This commit also adds 2 new tests for memory tagging in malloc on
> aarch64 and fixes a related small issue in __libc_free unblocking
> most straightforward use cases. Note, however, that memtag support
> in malloc remains experimental.
> ---
> elf/dl-tunables.list | 5 -
> malloc/arena.c | 5 +-
> malloc/malloc.c | 2 +-
> manual/tunables.texi | 36 ++----
> sysdeps/aarch64/Makefile | 1 +
> sysdeps/aarch64/cpu-features.h | 11 +-
> sysdeps/aarch64/dl-diagnostics-cpu.c | 3 +-
> sysdeps/aarch64/dl-mte.c | 65 +++++++++++
> sysdeps/aarch64/dl-prop.h | 11 ++
> sysdeps/aarch64/dl-start.S | 4 +
> sysdeps/aarch64/dl-tunables.list | 5 +
> sysdeps/aarch64/libc-mtag.h | 20 +++-
> sysdeps/generic/libc-mtag.h | 15 +++
> sysdeps/unix/sysv/linux/aarch64/Makefile | 17 +++
> .../unix/sysv/linux/aarch64/cpu-features.c | 51 +++++----
> .../unix/sysv/linux/aarch64/dl-procruntime.c | 16 +++
> sysdeps/unix/sysv/linux/aarch64/libc-start.h | 9 ++
> .../unix/sysv/linux/aarch64/tst-mte-helper.h | 104 ++++++++++++++++++
> .../unix/sysv/linux/aarch64/tst-mte-malloc.c | 68 ++++++++++++
> .../unix/sysv/linux/aarch64/tst-mte-realloc.c | 64 +++++++++++
> 20 files changed, 448 insertions(+), 64 deletions(-)
> create mode 100644 sysdeps/aarch64/dl-mte.c
> create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h
> create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
>
> diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
> index 040a544c0e..e4e4ed4c92 100644
> --- a/elf/dl-tunables.list
> +++ b/elf/dl-tunables.list
> @@ -108,11 +108,6 @@ glibc {
> }
>
> mem {
> - tagging {
> - type: INT_32
> - minval: 0
> - maxval: 255
> - }
> decorate_maps {
> type: INT_32
> minval: 0
> diff --git a/malloc/arena.c b/malloc/arena.c
> index 2f894d21e9..8c09aefce1 100644
> --- a/malloc/arena.c
> +++ b/malloc/arena.c
> @@ -253,14 +253,13 @@ __ptmalloc_init (void)
> #endif
>
> #ifdef USE_AARCH64_MTAG_HEAP
> - 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
> + /* If we should be using tagged memory
I think it makes sense to move the memory tag support to arch-specific
mode (GLRO(dl_aarch64_cpu_features).mte in this case).
> and that morecore does not support tagged regions, then
> disable it. */
> if (__MTAG_SBRK_UNTAGGED)
> __always_fail_morecore = true;
> -
Spurious line removal.
> mtag_enabled = true;
> mtag_mmap_flags = __MTAG_MMAP_FLAGS;
> }
> diff --git a/malloc/malloc.c b/malloc/malloc.c
> index 7de6599d30..0f5bc08d8c 100644
> --- a/malloc/malloc.c
> +++ b/malloc/malloc.c
> @@ -3341,7 +3341,7 @@ __libc_free (void *mem)
> }
>
> if (__glibc_unlikely (tcache_inactive ()))
> - return tcache_free_init (mem);
> + return tcache_free_init (chunk2mem (p));
Spurious indentation change.
> }
> #endif
>
> diff --git a/manual/tunables.texi b/manual/tunables.texi
> index 72769428e8..772148b2fa 100644
> --- a/manual/tunables.texi
> +++ b/manual/tunables.texi
> @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
> glibc.pthread.rseq: 1 (min: 0, max: 1)
> glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> -glibc.mem.tagging: 0 (min: 0, max: 255)
> glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max: 0xffffffffffffffff)
> glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> @@ -663,32 +662,21 @@ This tunable namespace supports operations that affect the way @theglibc{}
> and the process manage memory.
> @end deftp
>
> -@deftp Tunable glibc.mem.tagging
> -If the hardware supports memory tagging, this tunable can be used to
> -control the way @theglibc{} uses this feature. At present this is only
> -supported on AArch64 systems with the MTE extension; it is ignored for
> -all other systems.
> +@deftp Tunable glibc.mem.aarch64_mte
> +On AArch64 systems that support the Memory Tagging Extension (MTE) extension
> +this tunable allows to select the tag check fault mode (MTE mode). This
> +tunable only has effect when @theglibc{} has been configured with
> +@code{--enable-memory-tagging}.
>
> -This tunable takes a value between 0 and 255 and acts as a bitmask
> -that enables various capabilities.
> +Available values are:
>
> -Bit 0 (the least significant bit) causes the @code{malloc}
> -subsystem to allocate
> -tagged memory, with each allocation being assigned a random tag.
> -
> -Bit 1 enables precise faulting mode for tag violations on systems that
> -support deferred tag violation reporting. This may cause programs
> -to run more slowly.
> -
> -Bit 2 enables either precise or deferred faulting mode for tag violations
> -whichever is preferred by the system.
> -
> -Other bits are currently reserved.
> -
> -@Theglibc{} startup code will automatically enable memory tagging
> -support in the kernel if this tunable has any non-zero value.
> +@itemize @bullet
> +@item @code{none}: (the default), memory tagging is disabled.
> +@item @code{auto}: enable CPU preferred tag checking mode.
> +@item @code{sync}: enable synchronous tag check fault mode.
> +@item @code{async}: enable asynchronous tag check fault mode.
> +@end itemize
>
> -The default value is @samp{0}, which disables all memory tagging.
> @end deftp
>
> @deftp Tunable glibc.mem.decorate_maps
I think it makes sense to tie memory tagging support to arch-specific
tunables; it is not clear to me if/when Intel will implement similar
ChkTag support or if it will have similar faulting modes.
> 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/cpu-features.h b/sysdeps/aarch64/cpu-features.h
> index a6ec3eebe9..413f5aca82 100644
> --- a/sysdeps/aarch64/cpu-features.h
> +++ b/sysdeps/aarch64/cpu-features.h
> @@ -62,13 +62,20 @@ enum {
> BTI_CHECK_ENFORCED = 1,
> };
>
> +enum
> +{
> + MTE_TUNABLE_NONE = 0,
> + MTE_TUNABLE_AUTO = 1,
> + MTE_TUNABLE_SYNC = 2,
> + MTE_TUNABLE_ASYNC = 3,
> +};
> +
> 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 mte;
> bool sve;
> bool unused;
> bool mops;
> diff --git a/sysdeps/aarch64/dl-diagnostics-cpu.c b/sysdeps/aarch64/dl-diagnostics-cpu.c
> index 697868cb25..8e605f523f 100644
> --- a/sysdeps/aarch64/dl-diagnostics-cpu.c
> +++ b/sysdeps/aarch64/dl-diagnostics-cpu.c
> @@ -45,8 +45,7 @@ _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 ("mte", GLRO (dl_aarch64_cpu_features).mte);
> 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..000e3727f0
> --- /dev/null
> +++ b/sysdeps/aarch64/dl-mte.c
> @@ -0,0 +1,65 @@
> +/* AArch64 MTE (memory tagging) functions.
> + 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/>. */
> +
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> +
> +#include <ldsodefs.h>
> +#include <libc-mtag.h>
> +
> +/* 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)
> +
> +void
> +_dl_mte_check (struct link_map *l, const char *program)
> +{
> + if (!GLRO (dl_aarch64_cpu_features).mte)
> + return;
> + /* TODO: Update GL (dl_aarch64_mte) as necessary. */
> +}
> +
> +void __libc_mtag_init (void)
> +{
> + /* HWCAP2_MTE is not supported: nothing to do. */
> + if (!GLRO (dl_aarch64_cpu_features).mte)
> + return;
> + int t = GL (dl_aarch64_mte);
> + uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS;
> + switch (t)
> + {
> + case MTE_TUNABLE_NONE:
> + return;
> + case MTE_TUNABLE_AUTO:
> + flags |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC;
> + break;
> + case MTE_TUNABLE_SYNC:
> + flags |= PR_MTE_TCF_SYNC;
> + break;
> + case MTE_TUNABLE_ASYNC:
> + flags |= PR_MTE_TCF_ASYNC;
> + break;
> + default:
> + _dl_fatal_printf ("unknown MTE tunable value: %d\n", t);
> + }
> + 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);
> +}
> +
This moves MTE enable later in process creation, currently it is done
init_cpu_features issued by the loader; which now __libc_mtag_init will
be called during _start from the process.
It has the side-effect of disabling MTE hardening for some loader malloc
calls; after the self-relocation / __rtld_malloc_init_real call. This
will be same for a possible stack tagging support.
> +#endif /* USE_AARCH64_MTAG_HEAP || USE_AARCH64_MTAG_STACK */
> diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h
> index cf236df59b..e9e14a9693 100644
> --- a/sysdeps/aarch64/dl-prop.h
> +++ b/sysdeps/aarch64/dl-prop.h
> @@ -27,11 +27,19 @@ extern void _dl_bti_check (struct link_map *, const char *)
> extern void _dl_gcs_check (struct link_map *, const char *, int)
> attribute_hidden;
>
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> +extern void _dl_mte_check (struct link_map *, const char *)
> + attribute_hidden;
> +#endif
> +
> 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);
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> + _dl_mte_check (m, program);
> +#endif
> }
>
> static inline void __attribute__ ((always_inline))
> @@ -39,6 +47,9 @@ _dl_open_check (struct link_map *m, int dlopen_mode)
> {
> _dl_bti_check (m, NULL);
> _dl_gcs_check (m, NULL, dlopen_mode);
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> + _dl_mte_check (m, NULL);
> +#endif
> }
>
> static inline void __attribute__ ((always_inline))
> diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S
> index c278485cd3..e730dd6ec8 100644
> --- a/sysdeps/aarch64/dl-start.S
> +++ b/sysdeps/aarch64/dl-start.S
> @@ -66,6 +66,10 @@ ENTRY (_start)
> cbnz w0, L(failed_gcs_lock)
> L(skip_gcs_enable):
>
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> + bl __libc_mtag_init
> +#endif
> +
> .globl _dl_start_user
> .type _dl_start_user, %function
> _dl_start_user:
> diff --git a/sysdeps/aarch64/dl-tunables.list b/sysdeps/aarch64/dl-tunables.list
> index a2ccba0b29..e153387971 100644
> --- a/sysdeps/aarch64/dl-tunables.list
> +++ b/sysdeps/aarch64/dl-tunables.list
> @@ -34,4 +34,9 @@ glibc {
> default: 0
> }
> }
> + mem {
> + aarch64_mte {
> + type: STRING
> + }
> + }
> }
> diff --git a/sysdeps/aarch64/libc-mtag.h b/sysdeps/aarch64/libc-mtag.h
> index f821a2087e..69a05e8de4 100644
> --- a/sysdeps/aarch64/libc-mtag.h
> +++ b/sysdeps/aarch64/libc-mtag.h
> @@ -21,9 +21,17 @@
>
> #ifndef USE_AARCH64_MTAG_HEAP
> /* Generic bindings for systems that do not support memory tagging. */
> -#include_next "libc-mtag.h"
> +# include_next "libc-mtag.h"
> #else
>
> +#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
> +
> /* Used to ensure additional alignment when objects need to have distinct
> tags. */
> #define __MTAG_GRANULE_SIZE 16
> @@ -64,6 +72,16 @@ __libc_mtag_new_tag (void *p)
> return x0;
> }
>
> +/* Check if memory tagging is enabled. */
> +static inline bool
> +__libc_mtag_enabled (void)
> +{
> + return GL (dl_aarch64_mte) != MTE_TUNABLE_NONE;
> +}
> +
> +/* Init memory tagging support. */
> +void __libc_mtag_init (void);
> +
> #endif /* USE_AARCH64_MTAG_HEAP */
>
> #endif /* _AARCH64_LIBC_MTAG_H */
> diff --git a/sysdeps/generic/libc-mtag.h b/sysdeps/generic/libc-mtag.h
> index 5477bfa17f..85e51d17f1 100644
> --- a/sysdeps/generic/libc-mtag.h
> +++ b/sysdeps/generic/libc-mtag.h
> @@ -70,4 +70,19 @@ __libc_mtag_new_tag (void *p)
> return p;
> }
>
> +/* Generic version of check if memory tagging is enabled. */
> +static inline bool
> +__libc_mtag_enabled (void)
> +{
> + __libc_mtag_link_error ();
> + return false;
> +}
> +
> +/* Generic version of init memory tagging support. */
> +static inline void
> +__libc_mtag_init (void)
> +{
> + __libc_mtag_link_error ();
> +}
> +
> #endif /* _GENERIC_LIBC_MTAG_H */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile
> index 57461fded7..25d158237d 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/Makefile
> +++ b/sysdeps/unix/sysv/linux/aarch64/Makefile
> @@ -11,6 +11,23 @@ LDFLAGS-tst-tlsdesc-pac = -rdynamic
> $(objpfx)tst-tlsdesc-pac.out: $(objpfx)tst-tlsdesc-pac-mod.so
> endif
>
> +ifeq ($(subdir),malloc)
> +ifeq (yes,$(memory-tagging-heap))
> +
> +tests += \
> + tst-mte-malloc \
> + tst-mte-realloc \
> + # tests
> +
> +CFLAGS-tst-mte-malloc.o += -march=armv9-a+memtag
> +CFLAGS-tst-mte-realloc.o += -march=armv9-a+memtag
> +
> +tst-mte-malloc-ENV = GLIBC_TUNABLES=glibc.mem.aarch64_mte=sync
> +tst-mte-realloc-ENV = GLIBC_TUNABLES=glibc.mem.aarch64_mte=sync
> +
> +endif # ifeq (yes,$(memory-tagging-heap))
> +endif # ifeq ($(subdir),malloc)
> +
> ifeq ($(subdir),misc)
> sysdep_headers += sys/elf.h
> tests += \
> diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> index 62dcea3f94..d6dcc5ca85 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> @@ -20,7 +20,6 @@
> #include <cpu-features.h>
> #include <sys/auxv.h>
> #include <elf/dl-hwcaps.h>
> -#include <sys/prctl.h>
> #include <sys/utsname.h>
> #include <dl-tunables-parse.h>
> #include <dl-symbol-redir-ifunc.h>
> @@ -65,6 +64,21 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
> return UINT64_MAX;
> }
>
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> +static void
> +TUNABLE_CALLBACK (set_aarch64_mte) (tunable_val_t *val)
> +{
> + if (tunable_strcmp_cte (val, "auto"))
> + GL (dl_aarch64_mte) = MTE_TUNABLE_AUTO;
> + else if (tunable_strcmp_cte (val, "sync"))
> + GL (dl_aarch64_mte) = MTE_TUNABLE_SYNC;
> + else if (tunable_strcmp_cte (val, "async"))
> + GL (dl_aarch64_mte) = MTE_TUNABLE_ASYNC;
> + else
> + GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
> +}
> +#endif /* USE_AARCH64_MTAG_HEAP || USE_AARCH64_MTAG_STACK */
> +
> static inline void
> init_cpu_features (struct cpu_features *cpu_features)
> {
> @@ -101,31 +115,16 @@ 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_AARCH64_MTAG_HEAP
> - 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);
> + /* Check if MTE is supported. */
> + cpu_features->mte = 0;
> + GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> + if (GLRO (dl_hwcap2) & HWCAP2_MTE)
> + {
> + cpu_features->mte = 1;
> + TUNABLE_GET (glibc, mem, aarch64_mte, tunable_val_t *,
> + TUNABLE_CALLBACK (set_aarch64_mte));
> + }
> #endif /* USE_AARCH64_MTAG_HEAP */
>
> /* Check if SVE is supported. */
> diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> index 1f3b58d0fc..9689ddce25 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> @@ -35,3 +35,19 @@ PROCINFO_CLASS unsigned long _dl_aarch64_gcs
> ,
> # endif
> #endif
> +
> +#if !IS_IN (ldconfig)
> +# 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..5377462cda 100644
> --- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> +++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> @@ -36,6 +36,10 @@
> # define GCS_POLICY_OPTIONAL 2
> # endif
>
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> +# include <libc-mtag.h>
> +#endif
> +
> /* Must be on a top-level stack frame that does not return. */
> static inline void __attribute__((always_inline))
> aarch64_libc_setup_tls (void)
> @@ -72,6 +76,11 @@ aarch64_libc_setup_tls (void)
> _dl_fatal_printf ("failed to lock GCS: %d\n", -ret);
> }
> }
> +
> +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> + __libc_mtag_init ();
> +#endif
> +
> }
>
> # define ARCH_SETUP_IREL() apply_irel ()
> 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..eab7222464
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h
> @@ -0,0 +1,104 @@
> +/* AArch64 test helper functions for MTE.
> + 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 <support/check.h>
> +
> +#include <stdio.h>
> +#include <stdint.h>
> +#include <stdlib.h>
> +#include <malloc.h>
> +#include <sys/auxv.h>
> +#include <sys/prctl.h>
> +
> +#define GRANULE_SIZE 16
> +
> +/* Extract logical tag from pointer PTR. */
> +static __always_inline
> +uint64_t get_logical_tag (const void *ptr)
> +{
> + uint64_t t = (uint64_t)ptr;
> + return t >> 56ul & 0xf;
> +}
> +
> +/* Load allocation tag from memory pointed-to by the PTR pointer. */
> +static __always_inline
> +uint64_t get_allocation_tag (const void *ptr)
> +{
> + uint64_t t;
> + asm volatile ("ldg %0, [%1]" : "=r" (t) : "r" (ptr));
> + return t >> 56ul & 0xf;
> +}
> +
> +/* Read the Tag Check Override bit. */
> +static __always_inline
> +uint64_t get_pstate_tco (void) {
> + uint64_t t;
> + asm volatile ("mrs %0, tco" : "=r" (t));
> + return t;
> +}
> +
> +static __always_inline
> +void check_tags (void *tm)
> +{
> + size_t len = malloc_usable_size (tm);
> + TEST_VERIFY (len % GRANULE_SIZE == 0);
> +
> + printf ("tagged ptr: %p usable size: %zu\n", tm, len);
> +
> + uint64_t ltag = get_logical_tag (tm);
> + TEST_VERIFY (ltag != 0);
> +
> + for (size_t offset = 0; offset < len; offset += GRANULE_SIZE)
> + {
> + const char *g = (char *)tm + offset;
> + uint64_t atag = get_allocation_tag (g);
> + TEST_COMPARE (ltag, atag);
> + }
> +}
> +
> +static __always_inline
> +void check_mte_enabled (void)
> +{
> + /* Check if MTE is supported. */
> + if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE))
> + FAIL_UNSUPPORTED ("kernel or CPU does not support HWCAP2_MTE");
> +
> + /* Check if Tag Check Override bit is set. */
> + if (get_pstate_tco () != 0)
> + FAIL_UNSUPPORTED ("MTE tag check override is enabled");
> +
> + /* Check applied MTE params. */
> + uint64_t x = (uint64_t) prctl (PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
> + uint64_t status = (x & 1ul);
> + uint64_t mode = (x & PR_MTE_TCF_MASK) >> PR_MTE_TCF_SHIFT;
> + uint64_t tags = (x & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT;
> +
> + printf ("MTE status: %4lx\n", status);
> + printf ("MTE mode: %4lx\n", mode);
> + printf ("MTE tags: %4lx\n", tags);
> +
> + /* This test should be run in sync mode for tag checks. */
> + TEST_VERIFY (status == 1);
> + TEST_VERIFY (mode == PR_MTE_TCF_SYNC >> PR_MTE_TCF_SHIFT);
> + TEST_VERIFY (tags == 0xfffe);
> +}
> +
> +#endif // TST_MTE_HELPER_H
> \ No newline at end of file
> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c b/sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> new file mode 100644
> index 0000000000..e52a8c7fd9
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> @@ -0,0 +1,68 @@
> +/* AArch64 tests for heap memory tagging.
> + 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>
> +#include <support/support.h>
> +#include <support/xsignal.h>
> +#include <support/test-driver.h>
> +
> +#include "tst-mte-helper.h"
> +
> +static int
> +do_test (void)
> +{
> + /* Check if MTE is supported, configured and enabled. */
> + check_mte_enabled ();
> +
> + /* Tagged pointer. */
> + void *tm;
> +
> + tm = malloc (32);
> + puts ("testing malloc");
> + check_tags (tm);
> + free (tm);
> +
> + tm = calloc (2048, 16);
> + puts ("testing calloc");
> + check_tags (tm);
> + free (tm);
> +
> + tm = valloc (96);
> + puts ("testing valloc");
> + check_tags (tm);
> + free (tm);
> +
> + tm = aligned_alloc (16, 256);
> + puts ("testing aligned_alloc");
> + check_tags (tm);
> + free (tm);
> +
> + tm = memalign (16, 2048);
> + puts ("testing memalign");
> + check_tags (tm);
> + free (tm);
> +
> + tm = pvalloc (8196);
> + puts ("testing pvalloc");
> + check_tags (tm);
> + free (tm);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c b/sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
> new file mode 100644
> index 0000000000..c39ad5b3e3
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
> @@ -0,0 +1,64 @@
> +/* AArch64 tests for heap memory tagging.
> + 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>
> +#include <support/support.h>
> +#include <support/xsignal.h>
> +#include <support/test-driver.h>
> +
> +#include "tst-mte-helper.h"
> +
> +static int
> +do_test (void)
> +{
> +
> + /* Check if MTE is supported, configured and enabled. */
> + check_mte_enabled ();
> +
> + /* Tagged pointer. */
> + void *tm, *new_tm;
> + uint64_t ltag;
> +
> + tm = realloc (NULL, 4096);
> + ltag = get_logical_tag (tm);
> + puts ("testing realloc (NULL)");
> + check_tags (tm);
> +
> + /* Reduce size. */
> + new_tm = realloc (tm, 64);
> + puts ("testing realloc (decreased size)");
> + check_tags (new_tm);
> +
> + /* Check that realloc changes tag. */
> + TEST_VERIFY (ltag != get_logical_tag (new_tm));
> +
> + /* Increase size. */
> + new_tm = realloc (new_tm, 16000);
> + puts ("testing realloc (increased size)");
> + check_tags (new_tm);
> +
> + /* Check that realloc changes tag. */
> + TEST_VERIFY (ltag != get_logical_tag (new_tm));
> +
> + free (new_tm);
> +
> + return 0;
> +}
> +
> +#include <support/test-driver.c>
> +
On Wed, Mar 25, 2026 at 10:28 AM Adhemerval Zanella Netto <
adhemerval.zanella@linaro.org> wrote:
>
>
> On 20/03/26 08:36, Yury Khrustalev wrote:
> > The 'glibc.mem.tagging' tunable was added as a generic tunable although
> > at the time memory tagging was only supported by aarch64.
> >
> > The values for such a tunable are likely to be target-dependent.
> >
> > This commit removes 'glibc.mem.tagging' and instead adds a new target
> > specific tunable 'glibc.mem.aarch64_mte' that accepts string values:
> >
> > - 'none': memory tagging is disabled (the default).
> > - 'auto': enable CPU preferred tag checking mode.
> > - 'sync': enable synchronous tag check fault mode.
> > - 'async': enable asynchronous tag check fault mode.
> >
> > This tunable affects the flags used for the PR_TAGGED_ADDR_ENABLE
> > prctl syscall and only has effect when the system supports HWCAP2_MTE.
> >
> > This commit also changes when this prctl syscall happens. There are
> > three stages:
> >
> > 1) Check if the system supports HWCAP2_MTE and get the value of
> > the 'glibc.mem.aarch64_mte' tunable if it does.
> > 2) Check ELF marking and amend 'aarch64_mte' value if necessary
> > (note: this commit doesn't add any actual checks here, just
> > the way to implement them if required).
> > 3) Make the prctl syscall based on the 'aarch64_mte' value.
> >
> > If some MTE mode was requested but the prctl call failed, it is an error.
> >
> > The Glibc manual has been updated accordingly.
>
> This patch should be marked as RFC since it ties the MTE support to
> your proposed GNU attributes [1] and it still has missing semantic
> on how to enable GLRO(dl_aarch64_cpu_features).mte.
>
> [1] https://github.com/ARM-software/abi-aa/issues/382
>
> >
> > This commit also adds 2 new tests for memory tagging in malloc on
> > aarch64 and fixes a related small issue in __libc_free unblocking
> > most straightforward use cases. Note, however, that memtag support
> > in malloc remains experimental.
> > ---
> > elf/dl-tunables.list | 5 -
> > malloc/arena.c | 5 +-
> > malloc/malloc.c | 2 +-
> > manual/tunables.texi | 36 ++----
> > sysdeps/aarch64/Makefile | 1 +
> > sysdeps/aarch64/cpu-features.h | 11 +-
> > sysdeps/aarch64/dl-diagnostics-cpu.c | 3 +-
> > sysdeps/aarch64/dl-mte.c | 65 +++++++++++
> > sysdeps/aarch64/dl-prop.h | 11 ++
> > sysdeps/aarch64/dl-start.S | 4 +
> > sysdeps/aarch64/dl-tunables.list | 5 +
> > sysdeps/aarch64/libc-mtag.h | 20 +++-
> > sysdeps/generic/libc-mtag.h | 15 +++
> > sysdeps/unix/sysv/linux/aarch64/Makefile | 17 +++
> > .../unix/sysv/linux/aarch64/cpu-features.c | 51 +++++----
> > .../unix/sysv/linux/aarch64/dl-procruntime.c | 16 +++
> > sysdeps/unix/sysv/linux/aarch64/libc-start.h | 9 ++
> > .../unix/sysv/linux/aarch64/tst-mte-helper.h | 104 ++++++++++++++++++
> > .../unix/sysv/linux/aarch64/tst-mte-malloc.c | 68 ++++++++++++
> > .../unix/sysv/linux/aarch64/tst-mte-realloc.c | 64 +++++++++++
> > 20 files changed, 448 insertions(+), 64 deletions(-)
> > create mode 100644 sysdeps/aarch64/dl-mte.c
> > create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h
> > create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> > create mode 100644 sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
> >
> > diff --git a/elf/dl-tunables.list b/elf/dl-tunables.list
> > index 040a544c0e..e4e4ed4c92 100644
> > --- a/elf/dl-tunables.list
> > +++ b/elf/dl-tunables.list
> > @@ -108,11 +108,6 @@ glibc {
> > }
> >
> > mem {
> > - tagging {
> > - type: INT_32
> > - minval: 0
> > - maxval: 255
> > - }
> > decorate_maps {
> > type: INT_32
> > minval: 0
> > diff --git a/malloc/arena.c b/malloc/arena.c
> > index 2f894d21e9..8c09aefce1 100644
> > --- a/malloc/arena.c
> > +++ b/malloc/arena.c
> > @@ -253,14 +253,13 @@ __ptmalloc_init (void)
> > #endif
> >
> > #ifdef USE_AARCH64_MTAG_HEAP
> > - 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
> > + /* If we should be using tagged memory
>
> I think it makes sense to move the memory tag support to arch-specific
> mode (GLRO(dl_aarch64_cpu_features).mte in this case).
>
>
> > and that morecore does not support tagged regions, then
> > disable it. */
> > if (__MTAG_SBRK_UNTAGGED)
> > __always_fail_morecore = true;
> > -
>
> Spurious line removal.
>
> > mtag_enabled = true;
> > mtag_mmap_flags = __MTAG_MMAP_FLAGS;
> > }
> > diff --git a/malloc/malloc.c b/malloc/malloc.c
> > index 7de6599d30..0f5bc08d8c 100644
> > --- a/malloc/malloc.c
> > +++ b/malloc/malloc.c
> > @@ -3341,7 +3341,7 @@ __libc_free (void *mem)
> > }
> >
> > if (__glibc_unlikely (tcache_inactive ()))
> > - return tcache_free_init (mem);
> > + return tcache_free_init (chunk2mem (p));
>
> Spurious indentation change.
>
> > }
> > #endif
> >
> > diff --git a/manual/tunables.texi b/manual/tunables.texi
> > index 72769428e8..772148b2fa 100644
> > --- a/manual/tunables.texi
> > +++ b/manual/tunables.texi
> > @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> > glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max:
> 0xffffffffffffffff)
> > glibc.pthread.rseq: 1 (min: 0, max: 1)
> > glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> > -glibc.mem.tagging: 0 (min: 0, max: 255)
> > glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max:
> 0xffffffffffffffff)
> > glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > @@ -663,32 +662,21 @@ This tunable namespace supports operations that
> affect the way @theglibc{}
> > and the process manage memory.
> > @end deftp
> >
> > -@deftp Tunable glibc.mem.tagging
> > -If the hardware supports memory tagging, this tunable can be used to
> > -control the way @theglibc{} uses this feature. At present this is only
> > -supported on AArch64 systems with the MTE extension; it is ignored for
> > -all other systems.
> > +@deftp Tunable glibc.mem.aarch64_mte
> > +On AArch64 systems that support the Memory Tagging Extension (MTE)
> extension
> > +this tunable allows to select the tag check fault mode (MTE mode). This
> > +tunable only has effect when @theglibc{} has been configured with
> > +@code{--enable-memory-tagging}.
> >
> > -This tunable takes a value between 0 and 255 and acts as a bitmask
> > -that enables various capabilities.
> > +Available values are:
> >
> > -Bit 0 (the least significant bit) causes the @code{malloc}
> > -subsystem to allocate
> > -tagged memory, with each allocation being assigned a random tag.
> > -
> > -Bit 1 enables precise faulting mode for tag violations on systems that
> > -support deferred tag violation reporting. This may cause programs
> > -to run more slowly.
> > -
> > -Bit 2 enables either precise or deferred faulting mode for tag
> violations
> > -whichever is preferred by the system.
> > -
> > -Other bits are currently reserved.
> > -
>
I prefer generic Bit 0, 1 and 2..methods, where each architecture can
define its own meaning
depending on memory tagging design/feature.
Manuals can be updated in architecture specific ways.
For example:
- Bit 0: subsystem to allocate tagged memory, with each allocation being
assigned a random tag.
- Bit 1: In ARM it means precise faulting mode
- Bit 1: On x86, it could mean some other tagging feature.
and so on...
It is also extendable, if we need more feature bits in future.
--Sunil
> -@Theglibc{} startup code will automatically enable memory tagging
> > -support in the kernel if this tunable has any non-zero value.
> > +@itemize @bullet
> > +@item @code{none}: (the default), memory tagging is disabled.
> > +@item @code{auto}: enable CPU preferred tag checking mode.
> > +@item @code{sync}: enable synchronous tag check fault mode.
> > +@item @code{async}: enable asynchronous tag check fault mode.
> > +@end itemize
> >
> > -The default value is @samp{0}, which disables all memory tagging.
> > @end deftp
> >
> > @deftp Tunable glibc.mem.decorate_maps
>
> I think it makes sense to tie memory tagging support to arch-specific
> tunables; it is not clear to me if/when Intel will implement similar
> ChkTag support or if it will have similar faulting modes.
>
> > 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/cpu-features.h
> b/sysdeps/aarch64/cpu-features.h
> > index a6ec3eebe9..413f5aca82 100644
> > --- a/sysdeps/aarch64/cpu-features.h
> > +++ b/sysdeps/aarch64/cpu-features.h
> > @@ -62,13 +62,20 @@ enum {
> > BTI_CHECK_ENFORCED = 1,
> > };
> >
> > +enum
> > +{
> > + MTE_TUNABLE_NONE = 0,
> > + MTE_TUNABLE_AUTO = 1,
> > + MTE_TUNABLE_SYNC = 2,
> > + MTE_TUNABLE_ASYNC = 3,
> > +};
> > +
> > 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 mte;
> > bool sve;
> > bool unused;
> > bool mops;
> > diff --git a/sysdeps/aarch64/dl-diagnostics-cpu.c
> b/sysdeps/aarch64/dl-diagnostics-cpu.c
> > index 697868cb25..8e605f523f 100644
> > --- a/sysdeps/aarch64/dl-diagnostics-cpu.c
> > +++ b/sysdeps/aarch64/dl-diagnostics-cpu.c
> > @@ -45,8 +45,7 @@ _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 ("mte", GLRO (dl_aarch64_cpu_features).mte);
> > 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..000e3727f0
> > --- /dev/null
> > +++ b/sysdeps/aarch64/dl-mte.c
> > @@ -0,0 +1,65 @@
> > +/* AArch64 MTE (memory tagging) functions.
> > + 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/>. */
> > +
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > +
> > +#include <ldsodefs.h>
> > +#include <libc-mtag.h>
> > +
> > +/* 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)
> > +
> > +void
> > +_dl_mte_check (struct link_map *l, const char *program)
> > +{
> > + if (!GLRO (dl_aarch64_cpu_features).mte)
> > + return;
> > + /* TODO: Update GL (dl_aarch64_mte) as necessary. */
> > +}
> > +
> > +void __libc_mtag_init (void)
> > +{
> > + /* HWCAP2_MTE is not supported: nothing to do. */
> > + if (!GLRO (dl_aarch64_cpu_features).mte)
> > + return;
> > + int t = GL (dl_aarch64_mte);
> > + uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS;
> > + switch (t)
> > + {
> > + case MTE_TUNABLE_NONE:
> > + return;
> > + case MTE_TUNABLE_AUTO:
> > + flags |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC;
> > + break;
> > + case MTE_TUNABLE_SYNC:
> > + flags |= PR_MTE_TCF_SYNC;
> > + break;
> > + case MTE_TUNABLE_ASYNC:
> > + flags |= PR_MTE_TCF_ASYNC;
> > + break;
> > + default:
> > + _dl_fatal_printf ("unknown MTE tunable value: %d\n", t);
> > + }
> > + 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);
> > +}
> > +
>
> This moves MTE enable later in process creation, currently it is done
> init_cpu_features issued by the loader; which now __libc_mtag_init will
> be called during _start from the process.
>
> It has the side-effect of disabling MTE hardening for some loader malloc
> calls; after the self-relocation / __rtld_malloc_init_real call. This
> will be same for a possible stack tagging support.
>
> > +#endif /* USE_AARCH64_MTAG_HEAP || USE_AARCH64_MTAG_STACK */
> > diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h
> > index cf236df59b..e9e14a9693 100644
> > --- a/sysdeps/aarch64/dl-prop.h
> > +++ b/sysdeps/aarch64/dl-prop.h
> > @@ -27,11 +27,19 @@ extern void _dl_bti_check (struct link_map *, const
> char *)
> > extern void _dl_gcs_check (struct link_map *, const char *, int)
> > attribute_hidden;
> >
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > +extern void _dl_mte_check (struct link_map *, const char *)
> > + attribute_hidden;
> > +#endif
> > +
> > 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);
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > + _dl_mte_check (m, program);
> > +#endif
> > }
> >
> > static inline void __attribute__ ((always_inline))
> > @@ -39,6 +47,9 @@ _dl_open_check (struct link_map *m, int dlopen_mode)
> > {
> > _dl_bti_check (m, NULL);
> > _dl_gcs_check (m, NULL, dlopen_mode);
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > + _dl_mte_check (m, NULL);
> > +#endif
> > }
> >
> > static inline void __attribute__ ((always_inline))
> > diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S
> > index c278485cd3..e730dd6ec8 100644
> > --- a/sysdeps/aarch64/dl-start.S
> > +++ b/sysdeps/aarch64/dl-start.S
> > @@ -66,6 +66,10 @@ ENTRY (_start)
> > cbnz w0, L(failed_gcs_lock)
> > L(skip_gcs_enable):
> >
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > + bl __libc_mtag_init
> > +#endif
> > +
> > .globl _dl_start_user
> > .type _dl_start_user, %function
> > _dl_start_user:
> > diff --git a/sysdeps/aarch64/dl-tunables.list
> b/sysdeps/aarch64/dl-tunables.list
> > index a2ccba0b29..e153387971 100644
> > --- a/sysdeps/aarch64/dl-tunables.list
> > +++ b/sysdeps/aarch64/dl-tunables.list
> > @@ -34,4 +34,9 @@ glibc {
> > default: 0
> > }
> > }
> > + mem {
> > + aarch64_mte {
> > + type: STRING
> > + }
> > + }
> > }
> > diff --git a/sysdeps/aarch64/libc-mtag.h b/sysdeps/aarch64/libc-mtag.h
> > index f821a2087e..69a05e8de4 100644
> > --- a/sysdeps/aarch64/libc-mtag.h
> > +++ b/sysdeps/aarch64/libc-mtag.h
> > @@ -21,9 +21,17 @@
> >
> > #ifndef USE_AARCH64_MTAG_HEAP
> > /* Generic bindings for systems that do not support memory tagging. */
> > -#include_next "libc-mtag.h"
> > +# include_next "libc-mtag.h"
> > #else
> >
> > +#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
> > +
> > /* Used to ensure additional alignment when objects need to have
> distinct
> > tags. */
> > #define __MTAG_GRANULE_SIZE 16
> > @@ -64,6 +72,16 @@ __libc_mtag_new_tag (void *p)
> > return x0;
> > }
> >
> > +/* Check if memory tagging is enabled. */
> > +static inline bool
> > +__libc_mtag_enabled (void)
> > +{
> > + return GL (dl_aarch64_mte) != MTE_TUNABLE_NONE;
> > +}
> > +
> > +/* Init memory tagging support. */
> > +void __libc_mtag_init (void);
> > +
> > #endif /* USE_AARCH64_MTAG_HEAP */
> >
> > #endif /* _AARCH64_LIBC_MTAG_H */
> > diff --git a/sysdeps/generic/libc-mtag.h b/sysdeps/generic/libc-mtag.h
> > index 5477bfa17f..85e51d17f1 100644
> > --- a/sysdeps/generic/libc-mtag.h
> > +++ b/sysdeps/generic/libc-mtag.h
> > @@ -70,4 +70,19 @@ __libc_mtag_new_tag (void *p)
> > return p;
> > }
> >
> > +/* Generic version of check if memory tagging is enabled. */
> > +static inline bool
> > +__libc_mtag_enabled (void)
> > +{
> > + __libc_mtag_link_error ();
> > + return false;
> > +}
> > +
> > +/* Generic version of init memory tagging support. */
> > +static inline void
> > +__libc_mtag_init (void)
> > +{
> > + __libc_mtag_link_error ();
> > +}
> > +
> > #endif /* _GENERIC_LIBC_MTAG_H */
> > diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile
> b/sysdeps/unix/sysv/linux/aarch64/Makefile
> > index 57461fded7..25d158237d 100644
> > --- a/sysdeps/unix/sysv/linux/aarch64/Makefile
> > +++ b/sysdeps/unix/sysv/linux/aarch64/Makefile
> > @@ -11,6 +11,23 @@ LDFLAGS-tst-tlsdesc-pac = -rdynamic
> > $(objpfx)tst-tlsdesc-pac.out: $(objpfx)tst-tlsdesc-pac-mod.so
> > endif
> >
> > +ifeq ($(subdir),malloc)
> > +ifeq (yes,$(memory-tagging-heap))
> > +
> > +tests += \
> > + tst-mte-malloc \
> > + tst-mte-realloc \
> > + # tests
> > +
> > +CFLAGS-tst-mte-malloc.o += -march=armv9-a+memtag
> > +CFLAGS-tst-mte-realloc.o += -march=armv9-a+memtag
> > +
> > +tst-mte-malloc-ENV = GLIBC_TUNABLES=glibc.mem.aarch64_mte=sync
> > +tst-mte-realloc-ENV = GLIBC_TUNABLES=glibc.mem.aarch64_mte=sync
> > +
> > +endif # ifeq (yes,$(memory-tagging-heap))
> > +endif # ifeq ($(subdir),malloc)
> > +
> > ifeq ($(subdir),misc)
> > sysdep_headers += sys/elf.h
> > tests += \
> > diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> > index 62dcea3f94..d6dcc5ca85 100644
> > --- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> > +++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
> > @@ -20,7 +20,6 @@
> > #include <cpu-features.h>
> > #include <sys/auxv.h>
> > #include <elf/dl-hwcaps.h>
> > -#include <sys/prctl.h>
> > #include <sys/utsname.h>
> > #include <dl-tunables-parse.h>
> > #include <dl-symbol-redir-ifunc.h>
> > @@ -65,6 +64,21 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
> > return UINT64_MAX;
> > }
> >
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > +static void
> > +TUNABLE_CALLBACK (set_aarch64_mte) (tunable_val_t *val)
> > +{
> > + if (tunable_strcmp_cte (val, "auto"))
> > + GL (dl_aarch64_mte) = MTE_TUNABLE_AUTO;
> > + else if (tunable_strcmp_cte (val, "sync"))
> > + GL (dl_aarch64_mte) = MTE_TUNABLE_SYNC;
> > + else if (tunable_strcmp_cte (val, "async"))
> > + GL (dl_aarch64_mte) = MTE_TUNABLE_ASYNC;
> > + else
> > + GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
> > +}
> > +#endif /* USE_AARCH64_MTAG_HEAP || USE_AARCH64_MTAG_STACK */
> > +
> > static inline void
> > init_cpu_features (struct cpu_features *cpu_features)
> > {
> > @@ -101,31 +115,16 @@ 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_AARCH64_MTAG_HEAP
> > - 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);
> > + /* Check if MTE is supported. */
> > + cpu_features->mte = 0;
> > + GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > + if (GLRO (dl_hwcap2) & HWCAP2_MTE)
> > + {
> > + cpu_features->mte = 1;
> > + TUNABLE_GET (glibc, mem, aarch64_mte, tunable_val_t *,
> > + TUNABLE_CALLBACK (set_aarch64_mte));
> > + }
> > #endif /* USE_AARCH64_MTAG_HEAP */
> >
> > /* Check if SVE is supported. */
> > diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> > index 1f3b58d0fc..9689ddce25 100644
> > --- a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> > +++ b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
> > @@ -35,3 +35,19 @@ PROCINFO_CLASS unsigned long _dl_aarch64_gcs
> > ,
> > # endif
> > #endif
> > +
> > +#if !IS_IN (ldconfig)
> > +# 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..5377462cda 100644
> > --- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> > +++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
> > @@ -36,6 +36,10 @@
> > # define GCS_POLICY_OPTIONAL 2
> > # endif
> >
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > +# include <libc-mtag.h>
> > +#endif
> > +
> > /* Must be on a top-level stack frame that does not return. */
> > static inline void __attribute__((always_inline))
> > aarch64_libc_setup_tls (void)
> > @@ -72,6 +76,11 @@ aarch64_libc_setup_tls (void)
> > _dl_fatal_printf ("failed to lock GCS: %d\n", -ret);
> > }
> > }
> > +
> > +#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
> > + __libc_mtag_init ();
> > +#endif
> > +
> > }
> >
> > # define ARCH_SETUP_IREL() apply_irel ()
> > 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..eab7222464
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-helper.h
> > @@ -0,0 +1,104 @@
> > +/* AArch64 test helper functions for MTE.
> > + 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 <support/check.h>
> > +
> > +#include <stdio.h>
> > +#include <stdint.h>
> > +#include <stdlib.h>
> > +#include <malloc.h>
> > +#include <sys/auxv.h>
> > +#include <sys/prctl.h>
> > +
> > +#define GRANULE_SIZE 16
> > +
> > +/* Extract logical tag from pointer PTR. */
> > +static __always_inline
> > +uint64_t get_logical_tag (const void *ptr)
> > +{
> > + uint64_t t = (uint64_t)ptr;
> > + return t >> 56ul & 0xf;
> > +}
> > +
> > +/* Load allocation tag from memory pointed-to by the PTR pointer. */
> > +static __always_inline
> > +uint64_t get_allocation_tag (const void *ptr)
> > +{
> > + uint64_t t;
> > + asm volatile ("ldg %0, [%1]" : "=r" (t) : "r" (ptr));
> > + return t >> 56ul & 0xf;
> > +}
> > +
> > +/* Read the Tag Check Override bit. */
> > +static __always_inline
> > +uint64_t get_pstate_tco (void) {
> > + uint64_t t;
> > + asm volatile ("mrs %0, tco" : "=r" (t));
> > + return t;
> > +}
> > +
> > +static __always_inline
> > +void check_tags (void *tm)
> > +{
> > + size_t len = malloc_usable_size (tm);
> > + TEST_VERIFY (len % GRANULE_SIZE == 0);
> > +
> > + printf ("tagged ptr: %p usable size: %zu\n", tm, len);
> > +
> > + uint64_t ltag = get_logical_tag (tm);
> > + TEST_VERIFY (ltag != 0);
> > +
> > + for (size_t offset = 0; offset < len; offset += GRANULE_SIZE)
> > + {
> > + const char *g = (char *)tm + offset;
> > + uint64_t atag = get_allocation_tag (g);
> > + TEST_COMPARE (ltag, atag);
> > + }
> > +}
> > +
> > +static __always_inline
> > +void check_mte_enabled (void)
> > +{
> > + /* Check if MTE is supported. */
> > + if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE))
> > + FAIL_UNSUPPORTED ("kernel or CPU does not support HWCAP2_MTE");
> > +
> > + /* Check if Tag Check Override bit is set. */
> > + if (get_pstate_tco () != 0)
> > + FAIL_UNSUPPORTED ("MTE tag check override is enabled");
> > +
> > + /* Check applied MTE params. */
> > + uint64_t x = (uint64_t) prctl (PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
> > + uint64_t status = (x & 1ul);
> > + uint64_t mode = (x & PR_MTE_TCF_MASK) >> PR_MTE_TCF_SHIFT;
> > + uint64_t tags = (x & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT;
> > +
> > + printf ("MTE status: %4lx\n", status);
> > + printf ("MTE mode: %4lx\n", mode);
> > + printf ("MTE tags: %4lx\n", tags);
> > +
> > + /* This test should be run in sync mode for tag checks. */
> > + TEST_VERIFY (status == 1);
> > + TEST_VERIFY (mode == PR_MTE_TCF_SYNC >> PR_MTE_TCF_SHIFT);
> > + TEST_VERIFY (tags == 0xfffe);
> > +}
> > +
> > +#endif // TST_MTE_HELPER_H
> > \ No newline at end of file
> > diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> b/sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> > new file mode 100644
> > index 0000000000..e52a8c7fd9
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-malloc.c
> > @@ -0,0 +1,68 @@
> > +/* AArch64 tests for heap memory tagging.
> > + 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>
> > +#include <support/support.h>
> > +#include <support/xsignal.h>
> > +#include <support/test-driver.h>
> > +
> > +#include "tst-mte-helper.h"
> > +
> > +static int
> > +do_test (void)
> > +{
> > + /* Check if MTE is supported, configured and enabled. */
> > + check_mte_enabled ();
> > +
> > + /* Tagged pointer. */
> > + void *tm;
> > +
> > + tm = malloc (32);
> > + puts ("testing malloc");
> > + check_tags (tm);
> > + free (tm);
> > +
> > + tm = calloc (2048, 16);
> > + puts ("testing calloc");
> > + check_tags (tm);
> > + free (tm);
> > +
> > + tm = valloc (96);
> > + puts ("testing valloc");
> > + check_tags (tm);
> > + free (tm);
> > +
> > + tm = aligned_alloc (16, 256);
> > + puts ("testing aligned_alloc");
> > + check_tags (tm);
> > + free (tm);
> > +
> > + tm = memalign (16, 2048);
> > + puts ("testing memalign");
> > + check_tags (tm);
> > + free (tm);
> > +
> > + tm = pvalloc (8196);
> > + puts ("testing pvalloc");
> > + check_tags (tm);
> > + free (tm);
> > +
> > + return 0;
> > +}
> > +
> > +#include <support/test-driver.c>
> > diff --git a/sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
> b/sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
> > new file mode 100644
> > index 0000000000..c39ad5b3e3
> > --- /dev/null
> > +++ b/sysdeps/unix/sysv/linux/aarch64/tst-mte-realloc.c
> > @@ -0,0 +1,64 @@
> > +/* AArch64 tests for heap memory tagging.
> > + 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>
> > +#include <support/support.h>
> > +#include <support/xsignal.h>
> > +#include <support/test-driver.h>
> > +
> > +#include "tst-mte-helper.h"
> > +
> > +static int
> > +do_test (void)
> > +{
> > +
> > + /* Check if MTE is supported, configured and enabled. */
> > + check_mte_enabled ();
> > +
> > + /* Tagged pointer. */
> > + void *tm, *new_tm;
> > + uint64_t ltag;
> > +
> > + tm = realloc (NULL, 4096);
> > + ltag = get_logical_tag (tm);
> > + puts ("testing realloc (NULL)");
> > + check_tags (tm);
> > +
> > + /* Reduce size. */
> > + new_tm = realloc (tm, 64);
> > + puts ("testing realloc (decreased size)");
> > + check_tags (new_tm);
> > +
> > + /* Check that realloc changes tag. */
> > + TEST_VERIFY (ltag != get_logical_tag (new_tm));
> > +
> > + /* Increase size. */
> > + new_tm = realloc (new_tm, 16000);
> > + puts ("testing realloc (increased size)");
> > + check_tags (new_tm);
> > +
> > + /* Check that realloc changes tag. */
> > + TEST_VERIFY (ltag != get_logical_tag (new_tm));
> > +
> > + free (new_tm);
> > +
> > + return 0;
> > +}
> > +
> > +#include <support/test-driver.c>
> > +
>
>
On 25/03/26 14:50, Sunil Pandey wrote:
>
>
> On Wed, Mar 25, 2026 at 10:28 AM Adhemerval Zanella Netto <adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>> wrote:
> > diff --git a/manual/tunables.texi b/manual/tunables.texi
> > index 72769428e8..772148b2fa 100644
> > --- a/manual/tunables.texi
> > +++ b/manual/tunables.texi
> > @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> > glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
> > glibc.pthread.rseq: 1 (min: 0, max: 1)
> > glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> > -glibc.mem.tagging: 0 (min: 0, max: 255)
> > glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max: 0xffffffffffffffff)
> > glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > @@ -663,32 +662,21 @@ This tunable namespace supports operations that affect the way @theglibc{}
> > and the process manage memory.
> > @end deftp
> >
> > -@deftp Tunable glibc.mem.tagging
> > -If the hardware supports memory tagging, this tunable can be used to
> > -control the way @theglibc{} uses this feature. At present this is only
> > -supported on AArch64 systems with the MTE extension; it is ignored for
> > -all other systems.
> > +@deftp Tunable glibc.mem.aarch64_mte
> > +On AArch64 systems that support the Memory Tagging Extension (MTE) extension
> > +this tunable allows to select the tag check fault mode (MTE mode). This
> > +tunable only has effect when @theglibc{} has been configured with
> > +@code{--enable-memory-tagging}.
> >
> > -This tunable takes a value between 0 and 255 and acts as a bitmask
> > -that enables various capabilities.
> > +Available values are:
> >
> > -Bit 0 (the least significant bit) causes the @code{malloc}
> > -subsystem to allocate
> > -tagged memory, with each allocation being assigned a random tag.
> > -
> > -Bit 1 enables precise faulting mode for tag violations on systems that
> > -support deferred tag violation reporting. This may cause programs
> > -to run more slowly.
> > -
> > -Bit 2 enables either precise or deferred faulting mode for tag violations
> > -whichever is preferred by the system.
> > -
> > -Other bits are currently reserved.
> > -
>
>
> I prefer generic Bit 0, 1 and 2..methods, where each architecture can define its own meaning
> depending on memory tagging design/feature.
>
> Manuals can be updated in architecture specific ways.
>
> For example:
>
> - Bit 0: subsystem to allocate tagged memory, with each allocation being assigned a random tag.
>
> - Bit 1: In ARM it means precise faulting mode
> - Bit 1: On x86, it could mean some other tagging feature.
> and so on...
>
> It is also extendable, if we need more feature bits in future.
The patch makes it arch-specific, so x86 or any other architecture can define
its own semantic. I think it would make sense to keep current generic
glibc.mem.tagging if we have any indication that x86 or RISCV (which
seems the architecture that have similar memory tagging support in
ISA discussion) have similar support like similar faulting mode.
Do you know how x86 ChkTag ISA support should work wrt faulting mode? From
what I read, ChkTag also uses 4-bit tags, but it does not have
hardware-defined modes as MTE and all ChkTag memory access instruction
are inherently synchronous. It does not make much sense in adding a faulting
mode if hardware does not support it.
On Wed, Mar 25, 2026 at 11:15 AM Adhemerval Zanella Netto <
adhemerval.zanella@linaro.org> wrote:
>
>
> On 25/03/26 14:50, Sunil Pandey wrote:
> >
> >
> > On Wed, Mar 25, 2026 at 10:28 AM Adhemerval Zanella Netto <
> adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>>
> wrote:
>
> > > diff --git a/manual/tunables.texi b/manual/tunables.texi
> > > index 72769428e8..772148b2fa 100644
> > > --- a/manual/tunables.texi
> > > +++ b/manual/tunables.texi
> > > @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> > > glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max:
> 0xffffffffffffffff)
> > > glibc.pthread.rseq: 1 (min: 0, max: 1)
> > > glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> > > -glibc.mem.tagging: 0 (min: 0, max: 255)
> > > glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max:
> 0xffffffffffffffff)
> > > glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > @@ -663,32 +662,21 @@ This tunable namespace supports operations
> that affect the way @theglibc{}
> > > and the process manage memory.
> > > @end deftp
> > >
> > > -@deftp Tunable glibc.mem.tagging
> > > -If the hardware supports memory tagging, this tunable can be used
> to
> > > -control the way @theglibc{} uses this feature. At present this
> is only
> > > -supported on AArch64 systems with the MTE extension; it is
> ignored for
> > > -all other systems.
> > > +@deftp Tunable glibc.mem.aarch64_mte
> > > +On AArch64 systems that support the Memory Tagging Extension
> (MTE) extension
> > > +this tunable allows to select the tag check fault mode (MTE
> mode). This
> > > +tunable only has effect when @theglibc{} has been configured with
> > > +@code{--enable-memory-tagging}.
> > >
> > > -This tunable takes a value between 0 and 255 and acts as a bitmask
> > > -that enables various capabilities.
> > > +Available values are:
> > >
> > > -Bit 0 (the least significant bit) causes the @code{malloc}
> > > -subsystem to allocate
> > > -tagged memory, with each allocation being assigned a random tag.
> > > -
> > > -Bit 1 enables precise faulting mode for tag violations on systems
> that
> > > -support deferred tag violation reporting. This may cause programs
> > > -to run more slowly.
> > > -
> > > -Bit 2 enables either precise or deferred faulting mode for tag
> violations
> > > -whichever is preferred by the system.
> > > -
> > > -Other bits are currently reserved.
> > > -
> >
> >
> > I prefer generic Bit 0, 1 and 2..methods, where each architecture can
> define its own meaning
> > depending on memory tagging design/feature.
> >
> > Manuals can be updated in architecture specific ways.
> >
> > For example:
> >
> > - Bit 0: subsystem to allocate tagged memory, with each allocation being
> assigned a random tag.
> >
> > - Bit 1: In ARM it means precise faulting mode
> > - Bit 1: On x86, it could mean some other tagging feature.
> > and so on...
> >
> > It is also extendable, if we need more feature bits in future.
>
> The patch makes it arch-specific, so x86 or any other architecture can
> define
> its own semantic. I think it would make sense to keep current generic
> glibc.mem.tagging if we have any indication that x86 or RISCV (which
> seems the architecture that have similar memory tagging support in
> ISA discussion) have similar support like similar faulting mode.
>
> Do you know how x86 ChkTag ISA support should work wrt faulting mode? From
> what I read, ChkTag also uses 4-bit tags, but it does not have
> hardware-defined modes as MTE and all ChkTag memory access instruction
> are inherently synchronous. It does not make much sense in adding a
> faulting
> mode if hardware does not support it.
>
>
Even though x86 may not support exact faulting mode as ARM but top level
build configuration,
runtime tunables can be reused by defining architecture specific features
and keeping
external interface generic. i.e.
ARM:
Bit 0 (the least significant bit) causes the @code{malloc}
subsystem to allocate tagged memory, with each allocation being
assigned a random tag.
Bit 1 enables precise faulting mode for tag violations on systems that
support deferred tag violation reporting. This may cause programs to
run more slowly.
Bit 2 enables either precise or deferred faulting mode for tag
violations whichever is
preferred by the system.
Other bits are currently reserved.
x86:
Bit 0 (the least significant bit) causes the @code{malloc}
subsystem to allocate tagged memory.
Bit 1 Enable fault mode1
Bit 2 Enable fault mode2
Other bits are currently reserved.
On Wed, Mar 25, 2026 at 02:28:24PM -0300, Adhemerval Zanella Netto wrote:
>
> On 20/03/26 08:36, Yury Khrustalev wrote:
> > The 'glibc.mem.tagging' tunable was added as a generic tunable although
> > at the time memory tagging was only supported by aarch64.
> >
> > The values for such a tunable are likely to be target-dependent.
> >
> > This commit removes 'glibc.mem.tagging' and instead adds a new target
> > specific tunable 'glibc.mem.aarch64_mte' that accepts string values:
> >
> > - 'none': memory tagging is disabled (the default).
> > - 'auto': enable CPU preferred tag checking mode.
> > - 'sync': enable synchronous tag check fault mode.
> > - 'async': enable asynchronous tag check fault mode.
> >
> > This tunable affects the flags used for the PR_TAGGED_ADDR_ENABLE
> > prctl syscall and only has effect when the system supports HWCAP2_MTE.
> >
> > This commit also changes when this prctl syscall happens. There are
> > three stages:
> >
> > 1) Check if the system supports HWCAP2_MTE and get the value of
> > the 'glibc.mem.aarch64_mte' tunable if it does.
> > 2) Check ELF marking and amend 'aarch64_mte' value if necessary
> > (note: this commit doesn't add any actual checks here, just
> > the way to implement them if required).
> > 3) Make the prctl syscall based on the 'aarch64_mte' value.
> >
> > If some MTE mode was requested but the prctl call failed, it is an error.
> >
> > The Glibc manual has been updated accordingly.
>
> This patch should be marked as RFC since it ties the MTE support to
> your proposed GNU attributes [1]
I'm not sure what you mean, there is no mention of those proposed
attributes in this patch at all.
> and it still has missing semantic
> on how to enable GLRO(dl_aarch64_cpu_features).mte.
This internal flag was used as a copy of the value loaded from the
glibc.mem.tagging tunable. This wasn't useful and in this patch series
I change this field to merely indicate if HWCAP2_MTE is supported.
I'm not sure what you mean by
semantic on how to enable GLRO(dl_aarch64_cpu_features).mte
>
> [1] https://github.com/ARM-software/abi-aa/issues/382
>
> ...
>
> > diff --git a/malloc/arena.c b/malloc/arena.c
> > index 2f894d21e9..8c09aefce1 100644
> > --- a/malloc/arena.c
> > +++ b/malloc/arena.c
> > @@ -253,14 +253,13 @@ __ptmalloc_init (void)
> > #endif
> >
> > #ifdef USE_AARCH64_MTAG_HEAP
> > - 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
> > + /* If we should be using tagged memory
>
> I think it makes sense to move the memory tag support to arch-specific
> mode (GLRO(dl_aarch64_cpu_features).mte in this case).
We cannot use this arch-specific thing explicitly here, hence the
function '__libc_mtag_enabled'. There is also a reason why we don't
use 'GLRO(dl_aarch64_cpu_features).mte' inside that function.
>
> ...
>
> > + /* HWCAP2_MTE is not supported: nothing to do. */
> > + if (!GLRO (dl_aarch64_cpu_features).mte)
> > + return;
> > + int t = GL (dl_aarch64_mte);
> > + uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS;
> > + switch (t)
> > + {
> > + case MTE_TUNABLE_NONE:
> > + return;
> > + case MTE_TUNABLE_AUTO:
> > + flags |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC;
> > + break;
> > + case MTE_TUNABLE_SYNC:
> > + flags |= PR_MTE_TCF_SYNC;
> > + break;
> > + case MTE_TUNABLE_ASYNC:
> > + flags |= PR_MTE_TCF_ASYNC;
> > + break;
> > + default:
> > + _dl_fatal_printf ("unknown MTE tunable value: %d\n", t);
> > + }
> > + 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);
> > +}
> > +
>
> This moves MTE enable later in process creation, currently it is done
> init_cpu_features issued by the loader; which now __libc_mtag_init will
> be called during _start from the process.
If we want to take into account ELF marking (whatever form it comes in),
we cannot make prctl syscall in 'init_cpu_features' because markings are
scanned later. Also 'init_cpu_features' is not the best place for syscall
that may fail. And we cannot make more than 1 syscall with possibly
conflicting parameters.
> It has the side-effect of disabling MTE hardening for some loader malloc
> calls; after the self-relocation / __rtld_malloc_init_real call. This
> will be same for a possible stack tagging support.
This is the price we pay for using ELF marking.
Thanks,
Yury
On Wed, Mar 25, 2026 at 10:50:31AM -0700, Sunil Pandey wrote:
>
> ...
>
> > > diff --git a/manual/tunables.texi b/manual/tunables.texi
> > > index 72769428e8..772148b2fa 100644
> > > --- a/manual/tunables.texi
> > > +++ b/manual/tunables.texi
> > > @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> > > glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max:
> > 0xffffffffffffffff)
> > > glibc.pthread.rseq: 1 (min: 0, max: 1)
> > > glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> > > -glibc.mem.tagging: 0 (min: 0, max: 255)
> > > glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max:
> > 0xffffffffffffffff)
> > > glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > @@ -663,32 +662,21 @@ This tunable namespace supports operations that
> > affect the way @theglibc{}
> > > and the process manage memory.
> > > @end deftp
> > >
> > > -@deftp Tunable glibc.mem.tagging
> > > -If the hardware supports memory tagging, this tunable can be used to
> > > -control the way @theglibc{} uses this feature. At present this is only
> > > -supported on AArch64 systems with the MTE extension; it is ignored for
> > > -all other systems.
> > > +@deftp Tunable glibc.mem.aarch64_mte
> > > +On AArch64 systems that support the Memory Tagging Extension (MTE)
> > extension
> > > +this tunable allows to select the tag check fault mode (MTE mode). This
> > > +tunable only has effect when @theglibc{} has been configured with
> > > +@code{--enable-memory-tagging}.
> > >
> > > -This tunable takes a value between 0 and 255 and acts as a bitmask
> > > -that enables various capabilities.
> > > +Available values are:
> > >
> > > -Bit 0 (the least significant bit) causes the @code{malloc}
> > > -subsystem to allocate
> > > -tagged memory, with each allocation being assigned a random tag.
> > > -
> > > -Bit 1 enables precise faulting mode for tag violations on systems that
> > > -support deferred tag violation reporting. This may cause programs
> > > -to run more slowly.
> > > -
> > > -Bit 2 enables either precise or deferred faulting mode for tag
> > violations
> > > -whichever is preferred by the system.
> > > -
> > > -Other bits are currently reserved.
> > > -
> >
>
> I prefer generic Bit 0, 1 and 2..methods, where each architecture can
> define its own meaning
> depending on memory tagging design/feature.
>
> Manuals can be updated in architecture specific ways.
>
> For example:
>
> - Bit 0: subsystem to allocate tagged memory, with each allocation being
> assigned a random tag.
>
> - Bit 1: In ARM it means precise faulting mode
> - Bit 1: On x86, it could mean some other tagging feature.
> and so on...
>
> It is also extendable, if we need more feature bits in future.
>
> --Sunil
The tunable is moved to the aarch64 namespace. I agree that memory
tagging configuration will likely be target-specific, and this is the
reason why the tunable should be target-specific too.
Thanks,
Yury
On Wed, Mar 25, 2026 at 12:38:01PM -0700, Sunil Pandey wrote:
> On Wed, Mar 25, 2026 at 11:15 AM Adhemerval Zanella Netto <
> adhemerval.zanella@linaro.org> wrote:
>
> >
> >
> > On 25/03/26 14:50, Sunil Pandey wrote:
> > >
> > >
> > > On Wed, Mar 25, 2026 at 10:28 AM Adhemerval Zanella Netto <
> > adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>>
> > wrote:
> >
> > > > diff --git a/manual/tunables.texi b/manual/tunables.texi
> > > > index 72769428e8..772148b2fa 100644
> > > > --- a/manual/tunables.texi
> > > > +++ b/manual/tunables.texi
> > > > @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> > > > glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max:
> > 0xffffffffffffffff)
> > > > glibc.pthread.rseq: 1 (min: 0, max: 1)
> > > > glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> > > > -glibc.mem.tagging: 0 (min: 0, max: 255)
> > > > glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > > glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max:
> > 0xffffffffffffffff)
> > > > glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > > @@ -663,32 +662,21 @@ This tunable namespace supports operations
> > that affect the way @theglibc{}
> > > > and the process manage memory.
> > > > @end deftp
> > > >
> > > > -@deftp Tunable glibc.mem.tagging
> > > > -If the hardware supports memory tagging, this tunable can be used
> > to
> > > > -control the way @theglibc{} uses this feature. At present this
> > is only
> > > > -supported on AArch64 systems with the MTE extension; it is
> > ignored for
> > > > -all other systems.
> > > > +@deftp Tunable glibc.mem.aarch64_mte
> > > > +On AArch64 systems that support the Memory Tagging Extension
> > (MTE) extension
> > > > +this tunable allows to select the tag check fault mode (MTE
> > mode). This
> > > > +tunable only has effect when @theglibc{} has been configured with
> > > > +@code{--enable-memory-tagging}.
> > > >
> > > > -This tunable takes a value between 0 and 255 and acts as a bitmask
> > > > -that enables various capabilities.
> > > > +Available values are:
> > > >
> > > > -Bit 0 (the least significant bit) causes the @code{malloc}
> > > > -subsystem to allocate
> > > > -tagged memory, with each allocation being assigned a random tag.
> > > > -
> > > > -Bit 1 enables precise faulting mode for tag violations on systems
> > that
> > > > -support deferred tag violation reporting. This may cause programs
> > > > -to run more slowly.
> > > > -
> > > > -Bit 2 enables either precise or deferred faulting mode for tag
> > violations
> > > > -whichever is preferred by the system.
> > > > -
> > > > -Other bits are currently reserved.
> > > > -
> > >
> > >
> > > I prefer generic Bit 0, 1 and 2..methods, where each architecture can
> > define its own meaning
> > > depending on memory tagging design/feature.
> > >
> > > Manuals can be updated in architecture specific ways.
> > >
> > > For example:
> > >
> > > - Bit 0: subsystem to allocate tagged memory, with each allocation being
> > assigned a random tag.
> > >
> > > - Bit 1: In ARM it means precise faulting mode
> > > - Bit 1: On x86, it could mean some other tagging feature.
> > > and so on...
> > >
> > > It is also extendable, if we need more feature bits in future.
> >
> > The patch makes it arch-specific, so x86 or any other architecture can
> > define
> > its own semantic. I think it would make sense to keep current generic
> > glibc.mem.tagging if we have any indication that x86 or RISCV (which
> > seems the architecture that have similar memory tagging support in
> > ISA discussion) have similar support like similar faulting mode.
> >
> > Do you know how x86 ChkTag ISA support should work wrt faulting mode? From
> > what I read, ChkTag also uses 4-bit tags, but it does not have
> > hardware-defined modes as MTE and all ChkTag memory access instruction
> > are inherently synchronous. It does not make much sense in adding a
> > faulting
> > mode if hardware does not support it.
> >
> >
> Even though x86 may not support exact faulting mode as ARM but top level
> build configuration,
> runtime tunables can be reused by defining architecture specific features
> and keeping
> external interface generic. i.e.
>
> ARM:
>
> Bit 0 (the least significant bit) causes the @code{malloc}
> subsystem to allocate tagged memory, with each allocation being
> assigned a random tag.
>
> Bit 1 enables precise faulting mode for tag violations on systems that
> support deferred tag violation reporting. This may cause programs to
> run more slowly.
>
> Bit 2 enables either precise or deferred faulting mode for tag
> violations whichever is
> preferred by the system.
>
> Other bits are currently reserved.
>
> x86:
>
> Bit 0 (the least significant bit) causes the @code{malloc}
> subsystem to allocate tagged memory.
>
> Bit 1 Enable fault mode1
>
> Bit 2 Enable fault mode2
>
> Other bits are currently reserved.
We're going to have aarch64 tunable for memory tagging with
MTE-specific values. I don't mind keeping the existing generic
tunable 'glibc.mem.tagging', however it would be ignored on
aarch64, and I just thought it would be confusing to have it
since it's not used anywhere else.
Hope this helps,
Yury
On Thu, Mar 26, 2026 at 2:32 AM Yury Khrustalev <yury.khrustalev@arm.com>
wrote:
> On Wed, Mar 25, 2026 at 12:38:01PM -0700, Sunil Pandey wrote:
> > On Wed, Mar 25, 2026 at 11:15 AM Adhemerval Zanella Netto <
> > adhemerval.zanella@linaro.org> wrote:
> >
> > >
> > >
> > > On 25/03/26 14:50, Sunil Pandey wrote:
> > > >
> > > >
> > > > On Wed, Mar 25, 2026 at 10:28 AM Adhemerval Zanella Netto <
> > > adhemerval.zanella@linaro.org <mailto:adhemerval.zanella@linaro.org>>
> > > wrote:
> > >
> > > > > diff --git a/manual/tunables.texi b/manual/tunables.texi
> > > > > index 72769428e8..772148b2fa 100644
> > > > > --- a/manual/tunables.texi
> > > > > +++ b/manual/tunables.texi
> > > > > @@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
> > > > > glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max:
> > > 0xffffffffffffffff)
> > > > > glibc.pthread.rseq: 1 (min: 0, max: 1)
> > > > > glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
> > > > > -glibc.mem.tagging: 0 (min: 0, max: 255)
> > > > > glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > > > glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max:
> > > 0xffffffffffffffff)
> > > > > glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
> > > > > @@ -663,32 +662,21 @@ This tunable namespace supports
> operations
> > > that affect the way @theglibc{}
> > > > > and the process manage memory.
> > > > > @end deftp
> > > > >
> > > > > -@deftp Tunable glibc.mem.tagging
> > > > > -If the hardware supports memory tagging, this tunable can be
> used
> > > to
> > > > > -control the way @theglibc{} uses this feature. At present
> this
> > > is only
> > > > > -supported on AArch64 systems with the MTE extension; it is
> > > ignored for
> > > > > -all other systems.
> > > > > +@deftp Tunable glibc.mem.aarch64_mte
> > > > > +On AArch64 systems that support the Memory Tagging Extension
> > > (MTE) extension
> > > > > +this tunable allows to select the tag check fault mode (MTE
> > > mode). This
> > > > > +tunable only has effect when @theglibc{} has been configured
> with
> > > > > +@code{--enable-memory-tagging}.
> > > > >
> > > > > -This tunable takes a value between 0 and 255 and acts as a
> bitmask
> > > > > -that enables various capabilities.
> > > > > +Available values are:
> > > > >
> > > > > -Bit 0 (the least significant bit) causes the @code{malloc}
> > > > > -subsystem to allocate
> > > > > -tagged memory, with each allocation being assigned a random
> tag.
> > > > > -
> > > > > -Bit 1 enables precise faulting mode for tag violations on
> systems
> > > that
> > > > > -support deferred tag violation reporting. This may cause
> programs
> > > > > -to run more slowly.
> > > > > -
> > > > > -Bit 2 enables either precise or deferred faulting mode for tag
> > > violations
> > > > > -whichever is preferred by the system.
> > > > > -
> > > > > -Other bits are currently reserved.
> > > > > -
> > > >
> > > >
> > > > I prefer generic Bit 0, 1 and 2..methods, where each architecture can
> > > define its own meaning
> > > > depending on memory tagging design/feature.
> > > >
> > > > Manuals can be updated in architecture specific ways.
> > > >
> > > > For example:
> > > >
> > > > - Bit 0: subsystem to allocate tagged memory, with each allocation
> being
> > > assigned a random tag.
> > > >
> > > > - Bit 1: In ARM it means precise faulting mode
> > > > - Bit 1: On x86, it could mean some other tagging feature.
> > > > and so on...
> > > >
> > > > It is also extendable, if we need more feature bits in future.
> > >
> > > The patch makes it arch-specific, so x86 or any other architecture can
> > > define
> > > its own semantic. I think it would make sense to keep current generic
> > > glibc.mem.tagging if we have any indication that x86 or RISCV (which
> > > seems the architecture that have similar memory tagging support in
> > > ISA discussion) have similar support like similar faulting mode.
> > >
> > > Do you know how x86 ChkTag ISA support should work wrt faulting mode?
> From
> > > what I read, ChkTag also uses 4-bit tags, but it does not have
> > > hardware-defined modes as MTE and all ChkTag memory access instruction
> > > are inherently synchronous. It does not make much sense in adding a
> > > faulting
> > > mode if hardware does not support it.
> > >
> > >
> > Even though x86 may not support exact faulting mode as ARM but top level
> > build configuration,
> > runtime tunables can be reused by defining architecture specific
> features
> > and keeping
> > external interface generic. i.e.
> >
> > ARM:
> >
> > Bit 0 (the least significant bit) causes the @code{malloc}
> > subsystem to allocate tagged memory, with each allocation being
> > assigned a random tag.
> >
> > Bit 1 enables precise faulting mode for tag violations on systems
> that
> > support deferred tag violation reporting. This may cause programs
> to
> > run more slowly.
> >
> > Bit 2 enables either precise or deferred faulting mode for tag
> > violations whichever is
> > preferred by the system.
> >
> > Other bits are currently reserved.
> >
> > x86:
> >
> > Bit 0 (the least significant bit) causes the @code{malloc}
> > subsystem to allocate tagged memory.
> >
> > Bit 1 Enable fault mode1
> >
> > Bit 2 Enable fault mode2
> >
> > Other bits are currently reserved.
>
> We're going to have aarch64 tunable for memory tagging with
> MTE-specific values. I don't mind keeping the existing generic
> tunable 'glibc.mem.tagging', however it would be ignored on
> aarch64, and I just thought it would be confusing to have it
> since it's not used anywhere else.
>
> Hope this helps,
> Yury
>
>
My concerns are related to changes in architecture independent files.
diff --git a/config.h.in b/config.h.in
index b53731c393..2f51a40de1 100644
--- a/config.h.in
+++ b/config.h.in
@@ -182,8 +182,9 @@
/* Define if inlined system calls are available. */
#undef HAVE_INLINED_SYSCALLS
-/* Define if memory tagging support should be enabled. */
-#undef USE_MTAG
+/* Define if AArch64 memory tagging features should be enabled. */
+#undef USE_AARCH64_MTAG_HEAP
+#undef USE_AARCH64_MTAG_STACK
diff --git a/malloc/arena.c b/malloc/arena.c
index 727d0517c4..876568d720 100644
--- a/malloc/arena.c
+++ b/malloc/arena.c
@@ -252,19 +252,18 @@ __ptmalloc_init (void)
tcache_key_initialize ();
#endif
-#ifdef USE_MTAG
- if ((TUNABLE_GET_FULL (glibc, mem, tagging, int32_t, NULL) & 1) != 0)
+#ifdef USE_AARCH64_MTAG_HEAP
+ if (__libc_mtag_enabled ())
{
- /* If the tunable says that we should be using tagged memory
+ /* If we should be using tagged memory
and that morecore does not support tagged regions, then
disable it. */
if (__MTAG_SBRK_UNTAGGED)
__always_fail_morecore = true;
-
mtag_enabled = true;
mtag_mmap_flags = __MTAG_MMAP_FLAGS;
}
-#endif
+#endif /* USE_AARCH64_MTAG_HEAP */
#if defined SHARED && IS_IN (libc)
/* In case this libc copy is in a non-default namespace, never use
diff --git a/malloc/malloc.c b/malloc/malloc.c
index 6455a1b0e0..0f5bc08d8c 100644
--- a/malloc/malloc.c
+++ b/malloc/malloc.c
@@ -405,13 +405,13 @@ verify (PTRDIFF_MAX <= SIZE_MAX / 2);
tagging is not enabled, it simply returns the original pointer.
*/
-#ifdef USE_MTAG
+#ifdef USE_AARCH64_MTAG_HEAP
static bool mtag_enabled = false;
static int mtag_mmap_flags = 0;
#else
# define mtag_enabled false
# define mtag_mmap_flags 0
-#endif
+#endif /* USE_AARCH64_MTAG_HEAP */
static __always_inline void *
tag_region (void *ptr, size_t size)
@@ -3341,7 +3341,7 @@ __libc_free (void *mem)
}
if (__glibc_unlikely (tcache_inactive ()))
- return tcache_free_init (mem);
+ return tcache_free_init (chunk2mem (p));
}
Most guarded variables are useful across architecture, keeping generic
names will reduce code
duplication for other architecture.
Another comment, if you look into this call in malloc.c and whether it can
be replaced
From
return __libc_mtag_tag_zero_region (__libc_mtag_new_tag (ptr), size);
To
return __libc_mtag_tag_zero_region (ptr, size);
Avoiding function call in parameter.
--Sunil
Hello Sunil,
On Thu, Mar 26, 2026 at 10:22:36AM -0700, Sunil Pandey wrote:
> On Thu, Mar 26, 2026 at 2:32 AM Yury Khrustalev <yury.khrustalev@arm.com>
> wrote:
>
> ...
>
> > We're going to have aarch64 tunable for memory tagging with
> > MTE-specific values. I don't mind keeping the existing generic
> > tunable 'glibc.mem.tagging', however it would be ignored on
> > aarch64, and I just thought it would be confusing to have it
> > since it's not used anywhere else.
> >
> My concerns are related to changes in architecture independent files.
I understand your concerns and I agree that this is not ideal that some
target-dependent elements are included in the code that is supposed to
be generic.
However, this existed before my patch, I've only made it explicit by
changing the name of the macro. If you insist, I could keep the generic
name for this macro, but it would only hide the issue, not solve it.
We should probably use sysdeps files for such arch-dependent things,
however this would be one of the next steps.
>
> Most guarded variables are useful across architecture, keeping generic
> names will reduce code
> duplication for other architecture.
Agreed, but one step at a time.
>
> Another comment, if you look into this call in malloc.c and whether it can
> be replaced
>
> From
>
> return __libc_mtag_tag_zero_region (__libc_mtag_new_tag (ptr), size);
>
> To
>
> return __libc_mtag_tag_zero_region (ptr, size);
>
> Avoiding function call in parameter.
This is good point, but it's out of scope of this patch. I'll keep this
in mind for the future.
Thanks,
Yury
On Mon, Mar 30, 2026 at 1:04 AM Yury Khrustalev <yury.khrustalev@arm.com>
wrote:
> Hello Sunil,
>
> On Thu, Mar 26, 2026 at 10:22:36AM -0700, Sunil Pandey wrote:
> > On Thu, Mar 26, 2026 at 2:32 AM Yury Khrustalev <yury.khrustalev@arm.com
> >
> > wrote:
> >
> > ...
> >
> > > We're going to have aarch64 tunable for memory tagging with
> > > MTE-specific values. I don't mind keeping the existing generic
> > > tunable 'glibc.mem.tagging', however it would be ignored on
> > > aarch64, and I just thought it would be confusing to have it
> > > since it's not used anywhere else.
> > >
> > My concerns are related to changes in architecture independent files.
>
> I understand your concerns and I agree that this is not ideal that some
> target-dependent elements are included in the code that is supposed to
> be generic.
>
> However, this existed before my patch, I've only made it explicit by
> changing the name of the macro. If you insist, I could keep the generic
> name for this macro, but it would only hide the issue, not solve it.
>
>
How about renaming
USE_AARCH64_MTAG_HEAP => USE_MTAG_HEAP
USE_AARCH64_MTAG_STACK => USE_MTAG_STACK
Sunil
> We should probably use sysdeps files for such arch-dependent things,
> however this would be one of the next steps.
>
> >
> > Most guarded variables are useful across architecture, keeping generic
> > names will reduce code
> > duplication for other architecture.
>
> Agreed, but one step at a time.
>
> >
> > Another comment, if you look into this call in malloc.c and whether it
> can
> > be replaced
> >
> > From
> >
> > return __libc_mtag_tag_zero_region (__libc_mtag_new_tag (ptr), size);
> >
> > To
> >
> > return __libc_mtag_tag_zero_region (ptr, size);
> >
> > Avoiding function call in parameter.
>
> This is good point, but it's out of scope of this patch. I'll keep this
> in mind for the future.
>
Thanks
>
> Thanks,
> Yury
>
>
@@ -108,11 +108,6 @@ glibc {
}
mem {
- tagging {
- type: INT_32
- minval: 0
- maxval: 255
- }
decorate_maps {
type: INT_32
minval: 0
@@ -253,14 +253,13 @@ __ptmalloc_init (void)
#endif
#ifdef USE_AARCH64_MTAG_HEAP
- 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
+ /* If we should be using tagged memory
and that morecore does not support tagged regions, then
disable it. */
if (__MTAG_SBRK_UNTAGGED)
__always_fail_morecore = true;
-
mtag_enabled = true;
mtag_mmap_flags = __MTAG_MMAP_FLAGS;
}
@@ -3341,7 +3341,7 @@ __libc_free (void *mem)
}
if (__glibc_unlikely (tcache_inactive ()))
- return tcache_free_init (mem);
+ return tcache_free_init (chunk2mem (p));
}
#endif
@@ -40,7 +40,6 @@ glibc.malloc.perturb: 0 (min: 0, max: 255)
glibc.cpu.x86_shared_cache_size: 0x100000 (min: 0x0, max: 0xffffffffffffffff)
glibc.pthread.rseq: 1 (min: 0, max: 1)
glibc.cpu.prefer_map_32bit_exec: 0 (min: 0, max: 1)
-glibc.mem.tagging: 0 (min: 0, max: 255)
glibc.malloc.hugetlb: 0x0 (min: 0x0, max: 0xffffffffffffffff)
glibc.cpu.x86_rep_movsb_threshold: 0x2000 (min: 0x100, max: 0xffffffffffffffff)
glibc.malloc.mxfast: 0x0 (min: 0x0, max: 0xffffffffffffffff)
@@ -663,32 +662,21 @@ This tunable namespace supports operations that affect the way @theglibc{}
and the process manage memory.
@end deftp
-@deftp Tunable glibc.mem.tagging
-If the hardware supports memory tagging, this tunable can be used to
-control the way @theglibc{} uses this feature. At present this is only
-supported on AArch64 systems with the MTE extension; it is ignored for
-all other systems.
+@deftp Tunable glibc.mem.aarch64_mte
+On AArch64 systems that support the Memory Tagging Extension (MTE) extension
+this tunable allows to select the tag check fault mode (MTE mode). This
+tunable only has effect when @theglibc{} has been configured with
+@code{--enable-memory-tagging}.
-This tunable takes a value between 0 and 255 and acts as a bitmask
-that enables various capabilities.
+Available values are:
-Bit 0 (the least significant bit) causes the @code{malloc}
-subsystem to allocate
-tagged memory, with each allocation being assigned a random tag.
-
-Bit 1 enables precise faulting mode for tag violations on systems that
-support deferred tag violation reporting. This may cause programs
-to run more slowly.
-
-Bit 2 enables either precise or deferred faulting mode for tag violations
-whichever is preferred by the system.
-
-Other bits are currently reserved.
-
-@Theglibc{} startup code will automatically enable memory tagging
-support in the kernel if this tunable has any non-zero value.
+@itemize @bullet
+@item @code{none}: (the default), memory tagging is disabled.
+@item @code{auto}: enable CPU preferred tag checking mode.
+@item @code{sync}: enable synchronous tag check fault mode.
+@item @code{async}: enable asynchronous tag check fault mode.
+@end itemize
-The default value is @samp{0}, which disables all memory tagging.
@end deftp
@deftp Tunable glibc.mem.decorate_maps
@@ -4,6 +4,7 @@ ifeq ($(subdir),elf)
sysdep-dl-routines += \
dl-bti \
dl-gcs \
+ dl-mte \
# sysdep-dl-routines
tests += \
@@ -62,13 +62,20 @@ enum {
BTI_CHECK_ENFORCED = 1,
};
+enum
+{
+ MTE_TUNABLE_NONE = 0,
+ MTE_TUNABLE_AUTO = 1,
+ MTE_TUNABLE_SYNC = 2,
+ MTE_TUNABLE_ASYNC = 3,
+};
+
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 mte;
bool sve;
bool unused;
bool mops;
@@ -45,8 +45,7 @@ _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 ("mte", GLRO (dl_aarch64_cpu_features).mte);
print_cpu_features_value ("sve", GLRO (dl_aarch64_cpu_features).sve);
print_cpu_features_value ("zva_size",
GLRO (dl_aarch64_cpu_features).zva_size);
new file mode 100644
@@ -0,0 +1,65 @@
+/* AArch64 MTE (memory tagging) functions.
+ 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/>. */
+
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+
+#include <ldsodefs.h>
+#include <libc-mtag.h>
+
+/* 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)
+
+void
+_dl_mte_check (struct link_map *l, const char *program)
+{
+ if (!GLRO (dl_aarch64_cpu_features).mte)
+ return;
+ /* TODO: Update GL (dl_aarch64_mte) as necessary. */
+}
+
+void __libc_mtag_init (void)
+{
+ /* HWCAP2_MTE is not supported: nothing to do. */
+ if (!GLRO (dl_aarch64_cpu_features).mte)
+ return;
+ int t = GL (dl_aarch64_mte);
+ uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS;
+ switch (t)
+ {
+ case MTE_TUNABLE_NONE:
+ return;
+ case MTE_TUNABLE_AUTO:
+ flags |= PR_MTE_TCF_SYNC | PR_MTE_TCF_ASYNC;
+ break;
+ case MTE_TUNABLE_SYNC:
+ flags |= PR_MTE_TCF_SYNC;
+ break;
+ case MTE_TUNABLE_ASYNC:
+ flags |= PR_MTE_TCF_ASYNC;
+ break;
+ default:
+ _dl_fatal_printf ("unknown MTE tunable value: %d\n", t);
+ }
+ 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 /* USE_AARCH64_MTAG_HEAP || USE_AARCH64_MTAG_STACK */
@@ -27,11 +27,19 @@ extern void _dl_bti_check (struct link_map *, const char *)
extern void _dl_gcs_check (struct link_map *, const char *, int)
attribute_hidden;
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+extern void _dl_mte_check (struct link_map *, const char *)
+ attribute_hidden;
+#endif
+
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);
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+ _dl_mte_check (m, program);
+#endif
}
static inline void __attribute__ ((always_inline))
@@ -39,6 +47,9 @@ _dl_open_check (struct link_map *m, int dlopen_mode)
{
_dl_bti_check (m, NULL);
_dl_gcs_check (m, NULL, dlopen_mode);
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+ _dl_mte_check (m, NULL);
+#endif
}
static inline void __attribute__ ((always_inline))
@@ -66,6 +66,10 @@ ENTRY (_start)
cbnz w0, L(failed_gcs_lock)
L(skip_gcs_enable):
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+ bl __libc_mtag_init
+#endif
+
.globl _dl_start_user
.type _dl_start_user, %function
_dl_start_user:
@@ -34,4 +34,9 @@ glibc {
default: 0
}
}
+ mem {
+ aarch64_mte {
+ type: STRING
+ }
+ }
}
@@ -21,9 +21,17 @@
#ifndef USE_AARCH64_MTAG_HEAP
/* Generic bindings for systems that do not support memory tagging. */
-#include_next "libc-mtag.h"
+# include_next "libc-mtag.h"
#else
+#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
+
/* Used to ensure additional alignment when objects need to have distinct
tags. */
#define __MTAG_GRANULE_SIZE 16
@@ -64,6 +72,16 @@ __libc_mtag_new_tag (void *p)
return x0;
}
+/* Check if memory tagging is enabled. */
+static inline bool
+__libc_mtag_enabled (void)
+{
+ return GL (dl_aarch64_mte) != MTE_TUNABLE_NONE;
+}
+
+/* Init memory tagging support. */
+void __libc_mtag_init (void);
+
#endif /* USE_AARCH64_MTAG_HEAP */
#endif /* _AARCH64_LIBC_MTAG_H */
@@ -70,4 +70,19 @@ __libc_mtag_new_tag (void *p)
return p;
}
+/* Generic version of check if memory tagging is enabled. */
+static inline bool
+__libc_mtag_enabled (void)
+{
+ __libc_mtag_link_error ();
+ return false;
+}
+
+/* Generic version of init memory tagging support. */
+static inline void
+__libc_mtag_init (void)
+{
+ __libc_mtag_link_error ();
+}
+
#endif /* _GENERIC_LIBC_MTAG_H */
@@ -11,6 +11,23 @@ LDFLAGS-tst-tlsdesc-pac = -rdynamic
$(objpfx)tst-tlsdesc-pac.out: $(objpfx)tst-tlsdesc-pac-mod.so
endif
+ifeq ($(subdir),malloc)
+ifeq (yes,$(memory-tagging-heap))
+
+tests += \
+ tst-mte-malloc \
+ tst-mte-realloc \
+ # tests
+
+CFLAGS-tst-mte-malloc.o += -march=armv9-a+memtag
+CFLAGS-tst-mte-realloc.o += -march=armv9-a+memtag
+
+tst-mte-malloc-ENV = GLIBC_TUNABLES=glibc.mem.aarch64_mte=sync
+tst-mte-realloc-ENV = GLIBC_TUNABLES=glibc.mem.aarch64_mte=sync
+
+endif # ifeq (yes,$(memory-tagging-heap))
+endif # ifeq ($(subdir),malloc)
+
ifeq ($(subdir),misc)
sysdep_headers += sys/elf.h
tests += \
@@ -20,7 +20,6 @@
#include <cpu-features.h>
#include <sys/auxv.h>
#include <elf/dl-hwcaps.h>
-#include <sys/prctl.h>
#include <sys/utsname.h>
#include <dl-tunables-parse.h>
#include <dl-symbol-redir-ifunc.h>
@@ -65,6 +64,21 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
return UINT64_MAX;
}
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+static void
+TUNABLE_CALLBACK (set_aarch64_mte) (tunable_val_t *val)
+{
+ if (tunable_strcmp_cte (val, "auto"))
+ GL (dl_aarch64_mte) = MTE_TUNABLE_AUTO;
+ else if (tunable_strcmp_cte (val, "sync"))
+ GL (dl_aarch64_mte) = MTE_TUNABLE_SYNC;
+ else if (tunable_strcmp_cte (val, "async"))
+ GL (dl_aarch64_mte) = MTE_TUNABLE_ASYNC;
+ else
+ GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
+}
+#endif /* USE_AARCH64_MTAG_HEAP || USE_AARCH64_MTAG_STACK */
+
static inline void
init_cpu_features (struct cpu_features *cpu_features)
{
@@ -101,31 +115,16 @@ 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_AARCH64_MTAG_HEAP
- 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);
+ /* Check if MTE is supported. */
+ cpu_features->mte = 0;
+ GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+ if (GLRO (dl_hwcap2) & HWCAP2_MTE)
+ {
+ cpu_features->mte = 1;
+ TUNABLE_GET (glibc, mem, aarch64_mte, tunable_val_t *,
+ TUNABLE_CALLBACK (set_aarch64_mte));
+ }
#endif /* USE_AARCH64_MTAG_HEAP */
/* Check if SVE is supported. */
@@ -35,3 +35,19 @@ PROCINFO_CLASS unsigned long _dl_aarch64_gcs
,
# endif
#endif
+
+#if !IS_IN (ldconfig)
+# 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
@@ -36,6 +36,10 @@
# define GCS_POLICY_OPTIONAL 2
# endif
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+# include <libc-mtag.h>
+#endif
+
/* Must be on a top-level stack frame that does not return. */
static inline void __attribute__((always_inline))
aarch64_libc_setup_tls (void)
@@ -72,6 +76,11 @@ aarch64_libc_setup_tls (void)
_dl_fatal_printf ("failed to lock GCS: %d\n", -ret);
}
}
+
+#if defined USE_AARCH64_MTAG_HEAP || defined USE_AARCH64_MTAG_STACK
+ __libc_mtag_init ();
+#endif
+
}
# define ARCH_SETUP_IREL() apply_irel ()
new file mode 100644
@@ -0,0 +1,104 @@
+/* AArch64 test helper functions for MTE.
+ 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 <support/check.h>
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <malloc.h>
+#include <sys/auxv.h>
+#include <sys/prctl.h>
+
+#define GRANULE_SIZE 16
+
+/* Extract logical tag from pointer PTR. */
+static __always_inline
+uint64_t get_logical_tag (const void *ptr)
+{
+ uint64_t t = (uint64_t)ptr;
+ return t >> 56ul & 0xf;
+}
+
+/* Load allocation tag from memory pointed-to by the PTR pointer. */
+static __always_inline
+uint64_t get_allocation_tag (const void *ptr)
+{
+ uint64_t t;
+ asm volatile ("ldg %0, [%1]" : "=r" (t) : "r" (ptr));
+ return t >> 56ul & 0xf;
+}
+
+/* Read the Tag Check Override bit. */
+static __always_inline
+uint64_t get_pstate_tco (void) {
+ uint64_t t;
+ asm volatile ("mrs %0, tco" : "=r" (t));
+ return t;
+}
+
+static __always_inline
+void check_tags (void *tm)
+{
+ size_t len = malloc_usable_size (tm);
+ TEST_VERIFY (len % GRANULE_SIZE == 0);
+
+ printf ("tagged ptr: %p usable size: %zu\n", tm, len);
+
+ uint64_t ltag = get_logical_tag (tm);
+ TEST_VERIFY (ltag != 0);
+
+ for (size_t offset = 0; offset < len; offset += GRANULE_SIZE)
+ {
+ const char *g = (char *)tm + offset;
+ uint64_t atag = get_allocation_tag (g);
+ TEST_COMPARE (ltag, atag);
+ }
+}
+
+static __always_inline
+void check_mte_enabled (void)
+{
+ /* Check if MTE is supported. */
+ if (!(getauxval (AT_HWCAP2) & HWCAP2_MTE))
+ FAIL_UNSUPPORTED ("kernel or CPU does not support HWCAP2_MTE");
+
+ /* Check if Tag Check Override bit is set. */
+ if (get_pstate_tco () != 0)
+ FAIL_UNSUPPORTED ("MTE tag check override is enabled");
+
+ /* Check applied MTE params. */
+ uint64_t x = (uint64_t) prctl (PR_GET_TAGGED_ADDR_CTRL, 0, 0, 0, 0);
+ uint64_t status = (x & 1ul);
+ uint64_t mode = (x & PR_MTE_TCF_MASK) >> PR_MTE_TCF_SHIFT;
+ uint64_t tags = (x & PR_MTE_TAG_MASK) >> PR_MTE_TAG_SHIFT;
+
+ printf ("MTE status: %4lx\n", status);
+ printf ("MTE mode: %4lx\n", mode);
+ printf ("MTE tags: %4lx\n", tags);
+
+ /* This test should be run in sync mode for tag checks. */
+ TEST_VERIFY (status == 1);
+ TEST_VERIFY (mode == PR_MTE_TCF_SYNC >> PR_MTE_TCF_SHIFT);
+ TEST_VERIFY (tags == 0xfffe);
+}
+
+#endif // TST_MTE_HELPER_H
\ No newline at end of file
new file mode 100644
@@ -0,0 +1,68 @@
+/* AArch64 tests for heap memory tagging.
+ 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>
+#include <support/support.h>
+#include <support/xsignal.h>
+#include <support/test-driver.h>
+
+#include "tst-mte-helper.h"
+
+static int
+do_test (void)
+{
+ /* Check if MTE is supported, configured and enabled. */
+ check_mte_enabled ();
+
+ /* Tagged pointer. */
+ void *tm;
+
+ tm = malloc (32);
+ puts ("testing malloc");
+ check_tags (tm);
+ free (tm);
+
+ tm = calloc (2048, 16);
+ puts ("testing calloc");
+ check_tags (tm);
+ free (tm);
+
+ tm = valloc (96);
+ puts ("testing valloc");
+ check_tags (tm);
+ free (tm);
+
+ tm = aligned_alloc (16, 256);
+ puts ("testing aligned_alloc");
+ check_tags (tm);
+ free (tm);
+
+ tm = memalign (16, 2048);
+ puts ("testing memalign");
+ check_tags (tm);
+ free (tm);
+
+ tm = pvalloc (8196);
+ puts ("testing pvalloc");
+ check_tags (tm);
+ free (tm);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
new file mode 100644
@@ -0,0 +1,64 @@
+/* AArch64 tests for heap memory tagging.
+ 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>
+#include <support/support.h>
+#include <support/xsignal.h>
+#include <support/test-driver.h>
+
+#include "tst-mte-helper.h"
+
+static int
+do_test (void)
+{
+
+ /* Check if MTE is supported, configured and enabled. */
+ check_mte_enabled ();
+
+ /* Tagged pointer. */
+ void *tm, *new_tm;
+ uint64_t ltag;
+
+ tm = realloc (NULL, 4096);
+ ltag = get_logical_tag (tm);
+ puts ("testing realloc (NULL)");
+ check_tags (tm);
+
+ /* Reduce size. */
+ new_tm = realloc (tm, 64);
+ puts ("testing realloc (decreased size)");
+ check_tags (new_tm);
+
+ /* Check that realloc changes tag. */
+ TEST_VERIFY (ltag != get_logical_tag (new_tm));
+
+ /* Increase size. */
+ new_tm = realloc (new_tm, 16000);
+ puts ("testing realloc (increased size)");
+ check_tags (new_tm);
+
+ /* Check that realloc changes tag. */
+ TEST_VERIFY (ltag != get_logical_tag (new_tm));
+
+ free (new_tm);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
+