[PATCH 2/2] malloc: change tunable glibc.mem.tagging to glibc.mem.aarch64_mte

Sunil Pandey skpgkp2@gmail.com
Wed Mar 25 17:50:31 GMT 2026


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>
> > +
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://sourceware.org/pipermail/libc-alpha/attachments/20260325/1e6499ec/attachment-0001.htm>


More information about the Libc-alpha mailing list