[PATCH v2] rtld: Enable MTE for stack when specified in .dynamic

Cupertino Miranda cupertino.miranda@oracle.com
Mon Sep 1 21:10:07 GMT 2025


Hi everyone,

This is v2 of MTE stack support patch series.
This patch was adapted to conform with changes from:

  commit 3b2b88cceeb79f73a72367800d91599e2af4bb39
  elf: early conversion of elf p_flags to mprotect flags

It still does not introduce any tunable options.
They were initially discuss in v1 without any specific conclusions of
what should be done in that regard.

Looking forward to your reviews.

Cheers,
Cupertino

Changes from v1:
 - dropped changes related to stack creation for threads. No longer
   needed.
 - Adaped code to changes introduced with:
     "elf: early conversion of elf p_flags to mprotect flags"

MTE stack tagging enabled executables are flagged by setting the dynamic
array tag DT_AARCH64_MEMTAG_STACK as described in section 6 from:
https://github.com/ARM-software/abi-aa/blob/main/memtagabielf64/memtagabielf64.rst

Also the mode in which MTE would react to MTE faults is set by the tag
DT_AARCH64_MEMTAG_MODE.

This patch verifies for the presence of those tags in the dynamic array
and performs mprotect on the main application stack map with the extra
PROT_MTE flag.
---
 elf/elf.h                                     |  7 +-
 sysdeps/aarch64/Makefile                      |  3 +-
 sysdeps/aarch64/cpu-features.h                | 14 ++++
 sysdeps/aarch64/dl-mte.c                      | 78 +++++++++++++++++++
 sysdeps/aarch64/dl-prop.h                     |  4 +
 sysdeps/unix/sysv/linux/aarch64/Makefile      |  5 ++
 .../unix/sysv/linux/aarch64/dl-mte-stack.c    | 42 ++++++++++
 7 files changed, 151 insertions(+), 2 deletions(-)
 create mode 100644 sysdeps/aarch64/dl-mte.c
 create mode 100644 sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c

diff --git a/elf/elf.h b/elf/elf.h
index 2f29a47c0b..2c7b463769 100644
--- a/elf/elf.h
+++ b/elf/elf.h
@@ -3039,7 +3039,12 @@ enum
 #define DT_AARCH64_BTI_PLT	(DT_LOPROC + 1)
 #define DT_AARCH64_PAC_PLT	(DT_LOPROC + 3)
 #define DT_AARCH64_VARIANT_PCS	(DT_LOPROC + 5)
-#define DT_AARCH64_NUM		6
+#define DT_AARCH64_MEMTAG_MODE 	(DT_LOPROC + 9)
+#define DT_AARCH64_MEMTAG_HEAP 	(DT_LOPROC + 11)
+#define DT_AARCH64_MEMTAG_STACK 	(DT_LOPROC + 12)
+#define DT_AARCH64_MEMTAG_GLOBALS 	(DT_LOPROC + 13)
+#define DT_AARCH64_MEMTAG_GLOBALSSZ 	(DT_LOPROC + 15)
+#define DT_AARCH64_NUM		16
 
 /* AArch64 specific values for the st_other field.  */
 #define STO_AARCH64_VARIANT_PCS 0x80
diff --git a/sysdeps/aarch64/Makefile b/sysdeps/aarch64/Makefile
index bb97d31355..522813d6b9 100644
--- a/sysdeps/aarch64/Makefile
+++ b/sysdeps/aarch64/Makefile
@@ -11,7 +11,8 @@ endif
 ifeq ($(subdir),elf)
 sysdep-dl-routines += \
   dl-bti \
-  dl-gcs
+  dl-gcs \
+  dl-mte
 
 tests += tst-audit26 \
 	 tst-audit27
diff --git a/sysdeps/aarch64/cpu-features.h b/sysdeps/aarch64/cpu-features.h
index ef4e947e8c..b60b804e57 100644
--- a/sysdeps/aarch64/cpu-features.h
+++ b/sysdeps/aarch64/cpu-features.h
@@ -66,4 +66,18 @@ struct cpu_features
   bool mops;
 };
 
+#define ARCH_MTE_MODE_SYNC (1 << 1)
+#define ARCH_MTE_MODE_ASYNC (1 << 2)
+#define AARCH64_CPU_FEATURE_MTE_STATE_STACK (1 << 3)
+
+#define AARCH64_CPU_FEATURE_MTE_STATE_MODE_MASK (0x3)
+
+#define AARCH64_CPU_FEATURE_MTE_STATE_MODE_MASK (0x3)
+#define AARCH64_CPU_FEATURE_MTE_STATE_MODE_ASYNC (1)
+#define AARCH64_CPU_FEATURE_MTE_STATE_MODE_SYNC (1 << 1)
+
+#ifndef ARCH_INIT_MEMORY_STACK_TAGGING
+#define ARCH_INIT_MEMORY_STACK_TAGGING _dl_mte_stack_protect
+#endif
+
 #endif /* _CPU_FEATURES_AARCH64_H  */
diff --git a/sysdeps/aarch64/dl-mte.c b/sysdeps/aarch64/dl-mte.c
new file mode 100644
index 0000000000..b7d435bc53
--- /dev/null
+++ b/sysdeps/aarch64/dl-mte.c
@@ -0,0 +1,78 @@
+/* AArch64 MTE Stack functions.
+   Copyright (C) 2020-2025 Free Software Foundation, Inc.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <https://www.gnu.org/licenses/>.  */
+
+#include <unistd.h>
+#include <ldsodefs.h>
+#include <assert.h>
+#include <libintl.h>
+
+extern int
+_dl_mte_mode (void);
+
+void
+_dl_mte_stack_check (struct link_map *l, const char *program)
+{
+  ElfW(Dyn) *d;
+  bool mte_enabled = false;
+  bool mte_mode_selected = false;
+  for (d = l->l_ld; d->d_tag != DT_NULL; ++d)
+    {
+      if (d->d_tag == DT_AARCH64_MEMTAG_STACK)
+	{
+	  GLRO(dl_aarch64_cpu_features).mte_state |= AARCH64_CPU_FEATURE_MTE_STATE_STACK;
+	  mte_enabled = true;
+	}
+      if (d->d_tag == DT_AARCH64_MEMTAG_MODE)
+	{
+	  if (d->d_un.d_val == 1)
+	  {
+	    GLRO(dl_aarch64_cpu_features).mte_state &= ~AARCH64_CPU_FEATURE_MTE_STATE_MODE_MASK;
+	    GLRO(dl_aarch64_cpu_features).mte_state |= AARCH64_CPU_FEATURE_MTE_STATE_MODE_ASYNC;
+	  }
+	  else
+	  {
+	    GLRO(dl_aarch64_cpu_features).mte_state &= ~AARCH64_CPU_FEATURE_MTE_STATE_MODE_MASK;
+	    GLRO(dl_aarch64_cpu_features).mte_state |= AARCH64_CPU_FEATURE_MTE_STATE_MODE_SYNC;
+	  }
+	  mte_mode_selected = true;
+	}
+    }
+
+  if (mte_enabled && mte_mode_selected)
+    {
+      int errval = 0;
+      _dl_mte_mode ();
+      GL(dl_stack_prot_flags) |= PROT_MTE;
+
+      uintptr_t page = ((uintptr_t) __libc_stack_end
+	                & -(intptr_t) GLRO(dl_pagesize));
+
+      if (__mprotect ((void *) page, GLRO(dl_pagesize),
+		      GL(dl_stack_prot_flags)) != 0)
+	errval = errno;
+
+      if (errval)
+	{
+	  const char *errstring = NULL;
+	  errstring = N_("cannot set stack with PROT_MTE");
+	  const char *name = "MTE Stack";
+	  struct dl_exception exception;
+	  _dl_exception_create (&exception, name, errstring);
+	  _dl_signal_exception (errval, &exception, NULL);
+	}
+    }
+}
diff --git a/sysdeps/aarch64/dl-prop.h b/sysdeps/aarch64/dl-prop.h
index abca2be7fa..66b05c1b75 100644
--- a/sysdeps/aarch64/dl-prop.h
+++ b/sysdeps/aarch64/dl-prop.h
@@ -27,11 +27,15 @@ extern void _dl_bti_check (struct link_map *, const char *)
 extern void _dl_gcs_check (struct link_map *, const char *)
     attribute_hidden;
 
+extern void _dl_mte_stack_check (struct link_map *, const char *)
+    attribute_hidden;
+
 static inline void __attribute__ ((always_inline))
 _rtld_main_check (struct link_map *m, const char *program)
 {
   _dl_bti_check (m, program);
   _dl_gcs_check (m, program);
+  _dl_mte_stack_check (m, program);
 }
 
 static inline void __attribute__ ((always_inline))
diff --git a/sysdeps/unix/sysv/linux/aarch64/Makefile b/sysdeps/unix/sysv/linux/aarch64/Makefile
index 15a2b4471d..32bc043810 100644
--- a/sysdeps/unix/sysv/linux/aarch64/Makefile
+++ b/sysdeps/unix/sysv/linux/aarch64/Makefile
@@ -145,6 +145,11 @@ endif # ifeq ($(have-test-cc-gcs),yes)
 
 endif # ifeq ($(subdir),misc)
 
+ifeq ($(subdir),elf)
+sysdep-dl-routines += \
+  dl-mte-stack
+endif
+
 ifeq ($(subdir),stdlib)
 gen-as-const-headers += ucontext_i.sym
 endif
diff --git a/sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c b/sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c
new file mode 100644
index 0000000000..98fae85cfd
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/aarch64/dl-mte-stack.c
@@ -0,0 +1,42 @@
+/* Memory tagging handling for GNU dynamic linker.  Stub version.
+   Copyright (C) 2003-2025 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 <ldsodefs.h>
+#include <libintl.h>
+#include <not-cancel.h>
+#include <sys/prctl.h>
+
+#define MTE_ALLOWED_TAGS (0xfffe << PR_MTE_TAG_SHIFT)
+
+int
+_dl_mte_mode (void)
+{
+  int err = 0;
+  if (GLRO(dl_aarch64_cpu_features).mte_state & AARCH64_CPU_FEATURE_MTE_STATE_MODE_SYNC)
+    err = __prctl (PR_SET_TAGGED_ADDR_CTRL,
+	     (PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_SYNC | MTE_ALLOWED_TAGS),
+	     0, 0, 0);
+  else if (GLRO(dl_aarch64_cpu_features).mte_state & AARCH64_CPU_FEATURE_MTE_STATE_MODE_ASYNC)
+    err = __prctl (PR_SET_TAGGED_ADDR_CTRL,
+	     (PR_TAGGED_ADDR_ENABLE | PR_MTE_TCF_ASYNC | MTE_ALLOWED_TAGS),
+	     0, 0, 0);
+
+  return (err != 0
+	  && (GLRO(dl_aarch64_cpu_features).mte_state
+	      & AARCH64_CPU_FEATURE_MTE_STATE_MODE_MASK) != 0);
+}
-- 
2.30.2



More information about the Libc-alpha mailing list