[RFC 2/5] aarch64: check if clone3 supports shadow stack

Yury Khrustalev yury.khrustalev@arm.com
Tue Jun 10 15:13:17 GMT 2025


Rely on the version of the currently running kernel and use
existing mechanism to determine this version (same that was
used for checks related to SVE).

Since we only need this information in certain case, we try
to guard this additional code with macros and extra flag.
---
 sysdeps/aarch64/cpu-features.h                |  1 +
 .../unix/sysv/linux/aarch64/cpu-features.c    | 96 ++++++++++++++-----
 2 files changed, 74 insertions(+), 23 deletions(-)

diff --git a/sysdeps/aarch64/cpu-features.h b/sysdeps/aarch64/cpu-features.h
index ef4e947e8c..2dfda6f8f1 100644
--- a/sysdeps/aarch64/cpu-features.h
+++ b/sysdeps/aarch64/cpu-features.h
@@ -64,6 +64,7 @@ struct cpu_features
   bool sve;
   bool prefer_sve_ifuncs;
   bool mops;
+  bool clone3_has_shadow_stack;
 };
 
 #endif /* _CPU_FEATURES_AARCH64_H  */
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index 6d63c8a9ec..33bb2d1f66 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -63,19 +63,13 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
   return UINT64_MAX;
 }
 
-#if __LINUX_KERNEL_VERSION < 0x060200
-
-/* Return true if we prefer using SVE in string ifuncs.  Old kernels disable
-   SVE after every system call which results in unnecessary traps if memcpy
-   uses SVE.  This is true for kernels between 4.15.0 and before 6.2.0, except
-   for 5.14.0 which was patched.  For these versions return false to avoid using
-   SVE ifuncs.
-   Parse the kernel version into a 24-bit kernel.major.minor value without
-   calling any library functions.  If uname() is not supported or if the version
-   format is not recognized, assume the kernel is modern and return true.  */
+/* Parse the kernel version into a 24-bit kernel.major.minor value without
+   calling any library functions.  If uname() is not supported or if the
+   version format is not recognized, return 0 which indicates unknown
+   version. */
 
-static inline bool
-prefer_sve_ifuncs (void)
+static inline unsigned int
+get_current_kernel_version (void)
 {
   struct utsname buf;
   const char *p = &buf.release[0];
@@ -83,7 +77,7 @@ prefer_sve_ifuncs (void)
   int val;
 
   if (__uname (&buf) < 0)
-    return true;
+    return 0;
 
   for (int shift = 16; shift >= 0; shift -= 8)
     {
@@ -94,6 +88,25 @@ prefer_sve_ifuncs (void)
 	break;
     }
 
+  return kernel;
+}
+
+#if __LINUX_KERNEL_VERSION < 0x060200
+
+/* Return true if we prefer using SVE in string ifuncs.  Old kernels disable
+   SVE after every system call which results in unnecessary traps if memcpy
+   uses SVE.  This is true for kernels between 4.15.0 and before 6.2.0, except
+   for 5.14.0 which was patched.  For these versions return false to avoid using
+   SVE ifuncs.
+
+   If kernel version is unknown, assume the kernel is modern and return true.  */
+
+static inline bool
+prefer_sve_ifuncs (int kernel)
+{
+  if (kernel == 0x0)
+    /* Unknown version.  */
+    return true;
   if (kernel >= 0x060200 || kernel == 0x050e00)
     return true;
   if (kernel >= 0x040f00)
@@ -103,6 +116,23 @@ prefer_sve_ifuncs (void)
 
 #endif
 
+#if __LINUX_KERNEL_VERSION < 0x061000
+
+/* Kernel supports shadow stacks for threads allocated by userspace since
+   6.16 TBD. Starting with this version we can use shadow_stack_token in
+   struct clone_args.  */
+
+static inline bool
+clone3_has_shadow_stack (int kernel)
+{
+  if (kernel >= 0x061000)
+    return true;
+  return false;
+}
+
+#endif
+
+
 static inline void
 init_cpu_features (struct cpu_features *cpu_features)
 {
@@ -164,20 +194,40 @@ init_cpu_features (struct cpu_features *cpu_features)
 	     0, 0, 0);
 #endif
 
-  /* Check if SVE is supported.  */
-  cpu_features->sve = GLRO (dl_hwcap) & HWCAP_SVE;
+  /* Check if MOPS is supported.  */
+  cpu_features->mops = GLRO (dl_hwcap2) & HWCAP2_MOPS;
 
-  cpu_features->prefer_sve_ifuncs = cpu_features->sve;
+  uint64_t hwcap = GLRO (dl_hwcap);
+
+  /* We only need to determine current kernel version in a few
+     cases.  */
+#if  __LINUX_KERNEL_VERSION < 0x060200 \
+  || __LINUX_KERNEL_VERSION < 0x061000
+  bool need_kernel_ver = hwcap & (HWCAP_SVE | HWCAP_GCS);
+  unsigned int kernel __attribute__ ((unused));
+  kernel = need_kernel_ver ? get_current_kernel_version () : 0;
+#endif
 
+  /* Check if SVE is supported.  */
+  cpu_features->sve = hwcap & HWCAP_SVE;
+  cpu_features->prefer_sve_ifuncs = cpu_features->sve;
 #if __LINUX_KERNEL_VERSION < 0x060200
   if (cpu_features->sve)
-    cpu_features->prefer_sve_ifuncs = prefer_sve_ifuncs ();
+    cpu_features->prefer_sve_ifuncs = prefer_sve_ifuncs (kernel);
 #endif
 
-  /* Check if MOPS is supported.  */
-  cpu_features->mops = GLRO (dl_hwcap2) & HWCAP2_MOPS;
-
-  if (GLRO (dl_hwcap) & HWCAP_GCS)
-    /* GCS status may be updated later by binary compatibility checks.  */
-    GL (dl_aarch64_gcs) = TUNABLE_GET (glibc, cpu, aarch64_gcs, uint64_t, 0);
+  /* Check if GCS is supported.  */
+  if (hwcap & HWCAP_GCS)
+    {
+      cpu_features->clone3_has_shadow_stack = true;
+#if __LINUX_KERNEL_VERSION < 0x061000
+      /* It only makes sense to check for shadow stack support if
+       hardware supports it.  */
+      cpu_features->clone3_has_shadow_stack = clone3_has_shadow_stack (kernel);
+#endif
+      /* GCS status may be updated later by binary compatibility checks.  */
+      GL (dl_aarch64_gcs) = TUNABLE_GET (glibc, cpu, aarch64_gcs, uint64_t, 0);
+    }
+  else
+    cpu_features->clone3_has_shadow_stack = false;
 }
-- 
2.39.5



More information about the Libc-alpha mailing list