[PATCH 2/2] aarch64: Accept string values for glibc.cpu.aarch64_gcs tunable

Adhemerval Zanella adhemerval.zanella@linaro.org
Fri Mar 27 17:44:24 GMT 2026


This patch updates the glibc.cpu.aarch64_gcs tunable to accept
human-readable strings in addition to its standard numerical values.

The tunable now accepts the strings 'disabled', 'enforced', 'optional',
and 'override', mapping them to their corresponding 0, 1, 2, and 3 internal
enum states.

To support custom parsing in architecture-specific code, the
'tunable_parse_num' function is moved to the generic dl-tunables-parse.h
as an inline function.

The '__tunable_print_error' function is exposed globally so that invalid
string inputs caught in the newly added 'aarch64_gcs' callback can trigger
standard tunable warnings.

Checked on aarch64-linux-gnu.
---
 elf/dl-tunables.c                             | 29 +++++++++----------
 elf/dl-tunables.h                             |  3 ++
 manual/tunables.texi                          |  8 ++---
 sysdeps/aarch64/dl-tunables.list              |  5 +---
 sysdeps/generic/dl-tunables-parse.h           | 13 +++++++++
 .../unix/sysv/linux/aarch64/cpu-features.c    | 28 +++++++++++++++++-
 6 files changed, 61 insertions(+), 25 deletions(-)

diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index bdb1de4ceb..7fd6894ec9 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -36,6 +36,7 @@
 
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
+#include <dl-tunables-parse.h>
 
 static char **
 get_next_env (char **envp, char **name, char **val, char ***prev_envp)
@@ -119,17 +120,6 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
   cur->initialized = true;
 }
 
-static bool
-tunable_parse_num (const char *strval, size_t len, tunable_num_t *val)
-{
-  char *endptr = NULL;
-  uint64_t numval = _dl_strtoul (strval, &endptr);
-  if (endptr != strval + len)
-    return false;
-  *val = (tunable_num_t) numval;
-  return true;
-}
-
 /* Validate range of the input value and initialize the tunable CUR if it looks
    good.  */
 static bool
@@ -239,14 +229,21 @@ parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
   return ntunables;
 }
 
-static void
-parse_tunable_print_error (const struct tunable_toset_t *toset)
+
+void
+__tunable_print_error (const char *value, size_t len, const char *name)
 {
   _dl_error_printf ("WARNING: ld.so: invalid GLIBC_TUNABLES value `%.*s' "
 		    "for option `%s': ignored.\n",
-		    (int) toset->len,
-		    toset->value,
-		    toset->t->name);
+		    (int) len,
+		    value,
+		    name);
+}
+
+static inline void
+parse_tunable_print_error (const struct tunable_toset_t *toset)
+{
+  __tunable_print_error (toset->value, toset->len, toset->t->name);
 }
 
 static void
diff --git a/elf/dl-tunables.h b/elf/dl-tunables.h
index 45aeed47bc..9a1d12922e 100644
--- a/elf/dl-tunables.h
+++ b/elf/dl-tunables.h
@@ -61,6 +61,9 @@ rtld_hidden_proto (__tunable_get_val)
 rtld_hidden_proto (__tunable_set_val)
 rtld_hidden_proto (__tunable_get_default)
 
+extern void __tunable_print_error (const char *, size_t, const char *)
+  attribute_hidden;
+
 /* Define TUNABLE_GET and TUNABLE_SET in short form if TOP_NAMESPACE and
    TUNABLE_NAMESPACE are defined.  This is useful shorthand to get and set
    tunables within a module.  */
diff --git a/manual/tunables.texi b/manual/tunables.texi
index 72769428e8..b8befde341 100644
--- a/manual/tunables.texi
+++ b/manual/tunables.texi
@@ -605,12 +605,12 @@ This tunable controls Guarded Control Stack (GCS) for the process.
 Accepted values are:
 
 @itemize @bullet
-@item @code{0} = disabled: do not enable GCS.
-@item @code{1} = enforced: check markings and abort if any binary is not
+@item @code{0} or @code{disabled}: do not enable GCS.
+@item @code{1} or @code{enforced}: check markings and abort if any binary is not
 marked, otherwise enable GCS and lock all GCS features.
-@item @code{2} = optional: check markings but keep GCS off if any binary
+@item @code{2} or @code{optional}: check markings but keep GCS off if any binary
 is unmarked, otherwise enable GCS but do not lock any GCS features.
-@item @code{3} = override: enable GCS and lock all GCS features, markings
+@item @code{3} or @code{override}: enable GCS and lock all GCS features, markings
 are ignored.
 @end itemize
 
diff --git a/sysdeps/aarch64/dl-tunables.list b/sysdeps/aarch64/dl-tunables.list
index a2ccba0b29..302cbac437 100644
--- a/sysdeps/aarch64/dl-tunables.list
+++ b/sysdeps/aarch64/dl-tunables.list
@@ -28,10 +28,7 @@ glibc {
       default: 0
     }
     aarch64_gcs {
-      type: UINT_64
-      minval: 0
-      maxval: 3
-      default: 0
+      type: STRING
     }
   }
 }
diff --git a/sysdeps/generic/dl-tunables-parse.h b/sysdeps/generic/dl-tunables-parse.h
index 8ac49bff0b..f021036bbd 100644
--- a/sysdeps/generic/dl-tunables-parse.h
+++ b/sysdeps/generic/dl-tunables-parse.h
@@ -131,4 +131,17 @@ tunable_str_comma_strcmp (const struct tunable_str_comma_t *t, const char *str,
 #define tunable_str_comma_strcmp_cte(__t, __str) \
   tunable_str_comma_strcmp (__t, __str, sizeof (__str) - 1)
 
+static inline bool
+tunable_parse_num (const char *strval, size_t len, tunable_num_t *val)
+{
+  char *endptr = NULL;
+  uint64_t numval = _dl_strtoul (strval, &endptr);
+  if (endptr != strval + len)
+    return false;
+  *val = (tunable_num_t) numval;
+  return true;
+}
+#define tunable_parse_num_tun(__tunable, __ret) \
+  tunable_parse_num (__tunable->strval.str, __tunable->strval.len, __ret)
+
 #endif
diff --git a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
index 1e4f8a86b1..fe634cbded 100644
--- a/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
+++ b/sysdeps/unix/sysv/linux/aarch64/cpu-features.c
@@ -65,6 +65,31 @@ get_midr_from_mcpu (const struct tunable_str_t *mcpu)
   return UINT64_MAX;
 }
 
+static void
+TUNABLE_CALLBACK (aarch64_gcs) (tunable_val_t *valp)
+{
+  tunable_num_t val;
+  if (tunable_parse_num_tun (valp, &val))
+    {
+      if (tunable_val_lt (val, AARCH64_GCS_POLICY_DISABLED, true))
+	val = AARCH64_GCS_POLICY_DISABLED;
+      if (tunable_val_gt (val, AARCH64_GCS_POLICY_OVERRIDE, true))
+	val = AARCH64_GCS_POLICY_OVERRIDE;
+      GL(dl_aarch64_gcs) = val;
+    }
+  else if (tunable_strcmp_cte (valp, "disabled"))
+    GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_DISABLED;
+  else if (tunable_strcmp_cte (valp, "enforced"))
+    GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_ENFORCED;
+  else if (tunable_strcmp_cte (valp, "optional"))
+    GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_OPTIONAL;
+  else if (tunable_strcmp_cte (valp, "override"))
+    GL(dl_aarch64_gcs) = AARCH64_GCS_POLICY_OVERRIDE;
+  else
+    __tunable_print_error (valp->strval.str, valp->strval.len,
+			   "glibc.cpu.aarch64_gcs");
+}
+
 static inline void
 init_cpu_features (struct cpu_features *cpu_features)
 {
@@ -136,5 +161,6 @@ init_cpu_features (struct cpu_features *cpu_features)
 
   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);
+    TUNABLE_GET (glibc, cpu, aarch64_gcs, tunable_val_t *,
+		 TUNABLE_CALLBACK (aarch64_gcs));
 }
-- 
2.43.0



More information about the Libc-alpha mailing list