[v2,1/1] Add system-wide tunables: implement overridability

Message ID xnqzll4dxu.fsf@greed.delorie.com (mailing list archive)
State New
Headers
Series [v2,1/1] Add system-wide tunables: implement overridability |

Checks

Context Check Description
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit success Build for i686
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 success Test passed

Commit Message

DJ Delorie July 2, 2026, 3:19 p.m. UTC
  Andreas Schwab <schwab@suse.de> writes:
> That proves to be pretty fragile.

How about this?

    Add system-wide tunables: implement overridability
    
    Implement the overridability/nonoverridability flags
    for tunables.
  

Comments

Andreas Schwab July 6, 2026, 7:35 a.m. UTC | #1
On Jul 02 2026, DJ Delorie wrote:

> 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)
>  	{

This looks good.
  
Adhemerval Zanella Netto July 6, 2026, 9:06 p.m. UTC | #2
On 02/07/26 12:19, DJ Delorie wrote:
> 
> Andreas Schwab <schwab@suse.de> writes:
>> That proves to be pretty fragile.
> 
> How about this?
> 
>     Add system-wide tunables: implement overridability
>     
>     Implement the overridability/nonoverridability flags
>     for tunables.
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 789c504da9..7b00a0b931 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

No need to add ':' for each tunable, the TUNABLES rule already handles it.

The rest looks ok.

>  
>  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]);
>    }
>
  
DJ Delorie July 7, 2026, 3:27 a.m. UTC | #3
Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> writes:
> No need to add ':' for each tunable, the TUNABLES rule already handles it.

Removed.  I didn't know it did that...

> The rest looks ok.

Is this a reviewed-by or just commentary?
  
Adhemerval Zanella Netto July 7, 2026, 12:48 p.m. UTC | #4
On 07/07/26 00:27, DJ Delorie wrote:
> Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> writes:
>> No need to add ':' for each tunable, the TUNABLES rule already handles it.
> 
> Removed.  I didn't know it did that...
> 
>> The rest looks ok.
> 
> Is this a reviewed-by or just commentary?
> 

It is R-B, sorry for missing it:

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
  

Patch

diff --git a/elf/Makefile b/elf/Makefile
index 789c504da9..7b00a0b931 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]);
   }