[glibc/arm/malloc-mte-v3] aarch64: Add glibc.mem.aarch64_mte tunable

Yury Khrustalev via Glibc-cvs <[email protected]> Mon, 1 Jun 2026 11:43:32 +0000 (GMT)
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=ce61edbeea76c32965c4ba0ff110f38cc428e3a6

commit ce61edbeea76c32965c4ba0ff110f38cc428e3a6
Author: Yury Khrustalev <[email protected]>
Date:   Thu Apr 23 14:31:54 2026 +0100

    aarch64: Add glibc.mem.aarch64_mte tunable
    
    Add new tunable glibc.mem.aarch64_mte of string type with supported
    values: none, sync, async, and auto. Use it to control prctl syscall
    used to send the PR_SET_TAGGED_ADDR_CTRL command.
    
    To avoid confusion, we also remove the glibc.mem.tagging tunable.
    
    The new tunable defaults to 'none' and only works of MTE is supported.

Diff:
---
 manual/tunables.texi                             | 15 ++++++
 sysdeps/aarch64/Makefile                         |  1 +
 sysdeps/aarch64/cpu-features.h                   |  8 +++
 sysdeps/aarch64/dl-diagnostics-cpu.c             |  1 +
 sysdeps/aarch64/dl-mte.c                         | 67 ++++++++++++++++++++++++
 sysdeps/aarch64/dl-start.S                       |  2 +
 sysdeps/aarch64/dl-tunables.list                 |  5 ++
 sysdeps/aarch64/multiarch/init-arch.h            |  2 +-
 sysdeps/unix/sysv/linux/aarch64/cpu-features.c   | 21 ++++++++
 sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c | 16 ++++++
 sysdeps/unix/sysv/linux/aarch64/libc-start.h     |  6 +++
 11 files changed, 143 insertions(+), 1 deletion(-)

diff --git a/manual/tunables.texi b/manual/tunables.texi
index 95b7075ae7..cfaa5ca69d 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -682,6 +682,21 @@ This tunable takes a value of 0 and 1, where 1 enables the feature.
 The default value is @samp{0}, which disables the decoration.
 @end deftp
 
+@deftp Tunable glibc.mem.aarch64_mte
+On AArch64 systems that support Memory Tagging Extension (MTE) this
+tunable allows to select tag check fault mode (MTE mode).
+
+Supported values are:
+
+@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
+
+@end deftp
+
 @node gmon Tunables
 @section gmon Tunables
 @cindex gmon tunables
diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index 52ac85a75d..71e061850f 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 b7dab3dff4..372df464ce 100644
--- a/sysdeps/aarch64/cpu-features.h
+++ b/sysdeps/aarch64/cpu-features.h
@@ -59,6 +59,13 @@ 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;
@@ -69,6 +76,7 @@ struct cpu_features
   bool unused;
   bool mops;
   bool sve2;
+  bool mte;
 };
 
 #endif /* _CPU_FEATURES_AARCH64_H  */
diff --git a/sysdeps/aarch64/dl-diagnostics-cpu.c b/sysdeps/aarch64/dl-diagnostics-cpu.c
index 4b3fbcbd7c..6a08d62239 100644
--- a/sysdeps/aarch64/dl-diagnostics-cpu.c
+++ b/sysdeps/aarch64/dl-diagnostics-cpu.c
@@ -47,6 +47,7 @@ _dl_diagnostics_cpu (void)
   print_cpu_features_value ("mops", GLRO (dl_aarch64_cpu_features).mops);
   print_cpu_features_value ("sve", GLRO (dl_aarch64_cpu_features).sve);
   print_cpu_features_value ("sve2", GLRO (dl_aarch64_cpu_features).sve2);
+  print_cpu_features_value ("mte", GLRO (dl_aarch64_cpu_features).mte);
   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..41c4a260bb
--- /dev/null
+++ b/sysdeps/aarch64/dl-mte.c
@@ -0,0 +1,67 @@
+/* AArch64 implementation for MTE (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 <sys/cdefs.h>
+#include <ldsodefs.h>
+
+/* For the prctl syscall.  */
+#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)
+
+/* 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 __mte_init (void);
+rtld_hidden_proto (__mte_init)
+
+void __mte_init (void)
+{
+  if (!GLRO (dl_aarch64_cpu_features).mte)
+    return;
+  int mode = GL (dl_aarch64_mte);
+  if (mode == MTE_TUNABLE_NONE)
+    return;
+  uint64_t flags = PR_TAGGED_ADDR_ENABLE | MTE_ALLOWED_TAGS;
+  switch (mode)
+    {
+    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 mode: %d\n", mode);
+      __builtin_unreachable ();
+    }
+  /* We use inline system call to avoid unnecessary dependency
+     on the sys/prctl.h header.  */
+  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\n");
+}
+rtld_hidden_def (__mte_init)
diff --git a/sysdeps/aarch64/dl-start.S b/sysdeps/aarch64/dl-start.S
index c278485cd3..ab490d2460 100644
--- a/sysdeps/aarch64/dl-start.S
+++ b/sysdeps/aarch64/dl-start.S
@@ -66,6 +66,8 @@ ENTRY (_start)
 	cbnz	w0, L(failed_gcs_lock)
 L(skip_gcs_enable):
 
+	bl	HIDDEN_JUMPTARGET(__mte_init)
+
 .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/multiarch/init-arch.h b/sysdeps/aarch64/multiarch/init-arch.h
index e00d1746d8..1b87ad1d44 100644
--- a/sysdeps/aarch64/multiarch/init-arch.h
+++ b/sysdeps/aarch64/multiarch/init-arch.h
@@ -25,7 +25,7 @@
   unsigned __attribute__((unused)) zva_size =				      \
     GLRO(dl_aarch64_cpu_features).zva_size;				      \
   bool __attribute__((unused)) bti = GLRO(dl_aarch64_cpu_features).bti;	      \
-  bool __attribute__((unused)) mte = GLRO(dl_hwcap2) & HWCAP2_MTE;	      \
+  bool __attribute__((unused)) mte = GLRO(dl_aarch64_cpu_features).mte;	      \
   bool __attribute__((unused)) sve = GLRO(dl_aarch64_cpu_features).sve;	      \
   bool __attribute__((unused)) sve2 = GLRO(dl_aarch64_cpu_features).sve2;     \
   bool __attribute__((unused)) mops = GLRO(dl_aarch64_cpu_features).mops;
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index 0bc4addb2a..f81174ecab 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -59,6 +59,19 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
   return UINT64_MAX;
 }
 
+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;
+}
+
 static inline void
 init_cpu_features (struct cpu_features *cpu_features)
 {
@@ -95,6 +108,14 @@ 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);
 
+  /* Check if MTE is supported.  */
+  cpu_features->mte = GLRO (dl_hwcap2) & HWCAP2_MTE;
+  if (cpu_features->mte)
+    TUNABLE_GET (glibc, mem, aarch64_mte, tunable_val_t *,
+		 TUNABLE_CALLBACK (set_aarch64_mte));
+  else
+    GL (dl_aarch64_mte) = MTE_TUNABLE_NONE;
+
   /* Check if SVE is supported.  */
   cpu_features->sve = GLRO (dl_hwcap) & HWCAP_SVE;
   cpu_features->sve2 = GLRO (dl_hwcap2) & HWCAP2_SVE2;
diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c b/sysdeps/unix/sysv/linux/aarch64/dl-procruntime.c
index 1f3b58d0fc..0ddba83fb1 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 int _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 53683ee511..3a7422f70c 100644
--- a/sysdeps/unix/sysv/linux/aarch64/libc-start.h
+++ b/sysdeps/unix/sysv/linux/aarch64/libc-start.h
@@ -36,6 +36,9 @@
 #  define GCS_POLICY_OPTIONAL 2
 # endif
 
+void __mte_init (void);
+rtld_hidden_proto (__mte_init)
+
 /* 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 +75,9 @@ aarch64_libc_setup_tls (void)
 	    _dl_fatal_printf ("failed to lock GCS: %d\n", -ret);
 	}
     }
+
+  __mte_init ();
+
 }
 
 # define ARCH_SETUP_IREL() apply_irel ()