[glibc] Add system-wide tunables: implement overridability

DJ Delorie via Glibc-cvs <[email protected]> Thu, 9 Jul 2026 01:59:05 +0000 (GMT)
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=617376a04b28efe0e17c9227557ef6231735b0eb

commit 617376a04b28efe0e17c9227557ef6231735b0eb
Author: DJ Delorie <[email protected]>
Date:   Mon Jul 6 23:27:44 2026 -0400

    Add system-wide tunables: implement overridability
    
    Implement the overridability/nonoverridability flags
    for tunables.
    
    Reviewed-by: Adhemerval Zanella  <[email protected]>

Diff:
---
 elf/Makefile                            |  6 +++++-
 elf/dl-tunable-types.h                  |  2 ++
 elf/dl-tunables.c                       | 15 +++++++++++++++
 elf/tst-tunconf1.c                      | 20 ++++++++++++++++++++
 elf/tst-tunconf1.root/etc/tunables.conf |  8 ++++++++
 elf/tunconf.c                           | 26 ++++++++++++++++----------
 scripts/gen-tunables.awk                |  2 +-
 7 files changed, 67 insertions(+), 12 deletions(-)

diff --git a/elf/Makefile b/elf/Makefile
index e7b624ec57..01e77f2ca0 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -347,7 +347,11 @@ tests-static += \
   tst-tls9-static \
   # tests-static
 
-tst-tunconf1-TUNABLES-only = glibc.malloc.tcache_count=5
+tst-tunconf1-TUNABLES-only = \
+  glibc.malloc.tcache_count=5 \
+  glibc.malloc.perturb=41 \
+  glibc.malloc.mmap_threshold=10002 \
+  glibc.malloc.trim_threshold=10002
 
 static-dlopen-environment = \
   LD_LIBRARY_PATH=$(ld-library-path):$(common-objpfx)dlfcn
diff --git a/elf/dl-tunable-types.h b/elf/dl-tunable-types.h
index f421c01c26..3506eee512 100644
--- a/elf/dl-tunable-types.h
+++ b/elf/dl-tunable-types.h
@@ -65,6 +65,8 @@ struct _tunable
   tunable_val_t val;			/* The value.  */
   bool initialized;			/* Flag to indicate that the tunable is
 					   initialized.  */
+  bool locked;				/* If set, modifications are not
+					   allowed.  */
   /* Compatibility elements.  */
   const char env_alias[TUNABLE_ALIAS_MAX]; /* The compatibility environment
 					   variable name.  */
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 197d940d38..d19c2358c1 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -72,6 +72,9 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
 {
   tunable_num_t val, min, max;
 
+  if (cur->locked)
+    return;
+
   switch (cur->type.type_code)
     {
     case TUNABLE_TYPE_STRING:
@@ -409,6 +412,12 @@ __tunables_init (char **envp, char **argv)
 	    goto skip_due_to_filter;
 	  }
 
+	  /* If the tunable is set here, any previously set
+	     overridability flag is discarded.  We need to reset the
+	     overridability flag here so we can change the tunable,
+	     and may set it later if this tunable also locks it.  */
+	  tunable_list[tid].locked = false;
+
 	  /* See if the parsed type matches the desired type.  */
 	  if (tunable_list[tid].type.type_code == TUNABLE_TYPE_STRING)
 	    {
@@ -435,6 +444,12 @@ __tunables_init (char **envp, char **argv)
 		}
 	    }
 
+	  /* The overriability flag only applies to tunables
+	     which aren't filtered out.  */
+	  if ((tec->flags & TUNCONF_FLAG_OVERRIDABLE)
+	      == TUNCONF_OVERRIDE_DENY)
+	    tunable_list[tid].locked = true;
+
 	skip_due_to_filter:;
 	}
     }
diff --git a/elf/tst-tunconf1.c b/elf/tst-tunconf1.c
index c95a7cb8ba..74f596d913 100644
--- a/elf/tst-tunconf1.c
+++ b/elf/tst-tunconf1.c
@@ -26,10 +26,30 @@ do_test (void)
 {
   size_t tcache_count = TUNABLE_GET_FULL (glibc, malloc, tcache_count, size_t, NULL);
   size_t tcache_max = TUNABLE_GET_FULL (glibc, malloc, tcache_max, size_t, NULL);
+  size_t perturb = TUNABLE_GET_FULL (glibc, malloc, perturb, size_t, NULL);
+  size_t mmap_threshold = TUNABLE_GET_FULL (glibc, malloc, mmap_threshold, size_t, NULL);
+  size_t trim_threshold = TUNABLE_GET_FULL (glibc, malloc, trim_threshold, size_t, NULL);
+
   printf("tcache count is %ld (should be 5, from env)\n", (long)tcache_count);
   TEST_COMPARE ((long)tcache_count, 5);
   printf("tcache max is %ld (should be 4, from /etc)\n", (long)tcache_max);
   TEST_COMPARE ((long)tcache_max, 4);
+
+  /* This is set by the environment but blocked by the config.  */
+  printf("perturb is %ld (should be 42, from /etc)\n",
+	 (long)perturb);
+  TEST_COMPARE ((long)perturb, 42);
+
+  /* This is blocked by the general config, enabled by filter, set in env.  */
+  printf("mmap_threshold is %ld (should be 10002, from env)\n",
+	 (long)mmap_threshold);
+  TEST_COMPARE ((long)mmap_threshold, 10002);
+
+  /* This is allowed by the general config, blocked by filter, set in env.  */
+  printf("trim_threshold is %ld (should be 10001, from filter)\n",
+	 (long)trim_threshold);
+  TEST_COMPARE ((long)trim_threshold, 10001);
+
   return 0;
 }
 
diff --git a/elf/tst-tunconf1.root/etc/tunables.conf b/elf/tst-tunconf1.root/etc/tunables.conf
index f373a67902..f708a8fce4 100644
--- a/elf/tst-tunconf1.root/etc/tunables.conf
+++ b/elf/tst-tunconf1.root/etc/tunables.conf
@@ -8,7 +8,15 @@ $glibc.test_unsecure=1
 # These are checked inside the test case
 glibc.malloc.tcache_max=6
 $glibc.malloc.tcache_count=3
+
+-glibc.malloc.perturb=42
+-glibc.malloc.mmap_threshold=10000
+overridable glibc.malloc.trim_threshold=10000
+
 [proc:/bin/ls]
 glibc.malloc.tcache_max=7
+
 [proc:tst-tunconf1]
 glibc.malloc.tcache_max=4
++glibc.malloc.mmap_threshold=10001
+nonoverridable glibc.malloc.trim_threshold=10001
diff --git a/elf/tunconf.c b/elf/tunconf.c
index a5ca755abd..c27b50b1de 100644
--- a/elf/tunconf.c
+++ b/elf/tunconf.c
@@ -194,34 +194,40 @@ add_tunable (char *line, const char *filename, int lineno)
   /* Parse modifiers.  */
   while (*line)
     {
-      if (strncmp (line, "overridable ", 13) == 0)
+      int prefix_len;
+
+#define TUN_PREFIX(s)				\
+      prefix_len = sizeof(s) - 1,			\
+	strncmp (line, s " ", prefix_len + 1) == 0
+
+      if (TUN_PREFIX("overridable"))
 	{
 	  top = TOP_ALLOW;
 	  /* The line++ below skips the space.  */
-	  line += 12;
+	  line += prefix_len;
 	}
-      else if (strncmp (line, "nonoverridable ", 16) == 0)
+      else if (TUN_PREFIX ("nonoverridable"))
 	{
 	  top = TOP_DENY;
-	  line += 15;
+	  line += prefix_len;
 	}
-      else if (strncmp (line, "onlysecure ", 11) == 0)
+      else if (TUN_PREFIX ("onlysecure"))
 	{
 	  exclude_nonsecure = 1;
 	  exclude_secure = 0;
-	  line += 10;
+	  line += prefix_len;
 	}
-      else if (strncmp (line, "nonsecure ", 10) == 0)
+      else if (TUN_PREFIX ("nonsecure"))
 	{
 	  exclude_secure = 1;
 	  exclude_nonsecure = 0;
-	  line += 9;
+	  line += prefix_len;
 	}
-      else if (strncmp (line, "anysecure ", 10) == 0)
+      else if (TUN_PREFIX ("anysecure"))
 	{
 	  exclude_secure = 0;
 	  exclude_nonsecure = 0;
-	  line += 9;
+	  line += prefix_len;
 	}
       else switch (*line)
 	{
diff --git a/scripts/gen-tunables.awk b/scripts/gen-tunables.awk
index 5d34075c16..7c832303db 100644
--- a/scripts/gen-tunables.awk
+++ b/scripts/gen-tunables.awk
@@ -169,7 +169,7 @@ END {
     n = indices[2];
     m = indices[3];
     printf ("  {TUNABLE_NAME_S(%s, %s, %s)", t, n, m)
-    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, {%s}, false, %s},\n",
+    printf (", {TUNABLE_TYPE_%s, %s, %s}, {%s}, {%s}, false, false, %s},\n",
 	    types[t,n,m], minvals[t,n,m], maxvals[t,n,m], default_val[t,n,m],
 	    default_val[t,n,m], env_alias[t,n,m]);
   }