[glibc] Add system-wide tunables: Apply tunables part

DJ Delorie via Glibc-cvs <[email protected]> Tue, 30 Jun 2026 20:50:28 +0000 (GMT)
Newsgroups gmane.comp.lib.glibc.cvs
Message-ID <[email protected]>
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=9a953f9a48b4b8d1124dc4a8b7a8426157a7b1bd

commit 9a953f9a48b4b8d1124dc4a8b7a8426157a7b1bd
Author: DJ Delorie <[email protected]>
Date:   Thu Jun 25 23:30:39 2026 -0400

    Add system-wide tunables: Apply tunables part
    
    Load ld.so.cache and fetch the tunables extension.  Apply
    those tunables to the current program.  We do not yet apply
    security policies.
    
    Reviewed-by: Arjun Shankar <[email protected]>

Diff:
---
 elf/dl-cache.c    | 54 +++++++++++++++++++++++++++++++++++++++++++++
 elf/dl-tunables.c | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 elf/tunconf.h     |  3 +++
 3 files changed, 122 insertions(+)

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index d652ba5d7e..d05afe2700 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -28,6 +28,7 @@
 #include <dl-isa-level.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include "tunconf.h"
 
 /* This is the starting address and the size of the mmap()ed file.  */
 static struct cache_file *cache;
@@ -613,3 +614,56 @@ _dl_unload_cache (void)
      now.  */
 }
 #endif
+
+const struct tunable_header_cached *
+_dl_load_cache_tunables (const char **data)
+{
+  struct cache_extension_all_loaded ext;
+  struct tunable_header_cached *thc;
+  struct tunable_entry_cached *tec;
+  int i, count;
+
+  if (_dl_check_ldsocache_needs_loading ())
+    _dl_maybe_load_ldsocache ();
+
+  if (cache_new)
+    *data = (const char *) cache_new;
+  else
+    return NULL;
+
+  if (!cache_extension_load (cache_new, cache, cachesize, &ext))
+    return NULL;
+
+  /* Validate length/contents here. */
+  if (ext.sections[cache_extension_tag_tunables].size
+      < sizeof(struct tunable_header_cached))
+    return NULL;
+
+  thc = (struct tunable_header_cached *)
+    ext.sections[cache_extension_tag_tunables].base;
+  tec = thc->tunables;
+  count = thc->num_tunables;
+
+  if (ext.sections[cache_extension_tag_tunables].base
+      + ext.sections[cache_extension_tag_tunables].size
+      != (void *) & tec[count])
+    return NULL;
+
+  /* Validate each entry.  */
+  int s_start = (const char *) (&cache_new->libs[cache_new->nlibs]) - *data;
+  int s_end = s_start + cache_new->len_strings;
+  for (i = 0; i < count; i ++)
+    {
+      if (thc->tunables[i].name_offset < s_start
+	  || thc->tunables[i].name_offset >= s_end
+	  || thc->tunables[i].value_offset < s_start
+	  || thc->tunables[i].value_offset >= s_end)
+	return NULL;
+      if (thc->tunables[i].flag_offset != 0
+	  && (thc->tunables[i].flag_offset < s_start
+	      || thc->tunables[i].flag_offset >= s_end))
+	return NULL;
+    }
+
+  return thc;
+}
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 5c65e8b458..f183e99855 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -37,6 +37,7 @@
 
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
+#include "tunconf.h"
 
 static char **
 get_next_env (char **envp, char **name, char **val, char ***prev_envp)
@@ -302,6 +303,70 @@ __tunables_init (char **envp)
   if (MALLOC_DEFAULT_THP_PAGESIZE > 0)
     TUNABLE_SET (glibc, malloc, hugetlb, 1);
 
+#if defined(SHARED) && defined (USE_LDCONFIG)
+  const struct tunable_header_cached *thc;
+  const char *td;
+
+  thc = _dl_load_cache_tunables (&td);
+  if (thc != NULL)
+    {
+      for (int t = 0; t < thc->num_tunables; ++ t)
+	{
+	  const struct tunable_entry_cached *tec = &( thc->tunables[t] );
+	  int tid = tec->tunable_id;
+	  const char *name = td + tec->name_offset;
+	  const char *value = td + tec->value_offset;
+
+	  /* Check that we have the correct tunable, and search by
+	     name if needed.  We rely on order of operations here to
+	     avoid mis-indexing tunables[].  */
+	  if (tid < 0 || tid >= tunables_list_size
+	      || strcmp (name, tunable_list[tid].name) != 0)
+	    {
+	      /* It does not, search by name instead.  */
+	      tid = -1;
+	      for (int i = 0; i < tunables_list_size; i++)
+		{
+		  if (strcmp (name, tunable_list[i].name) == 0)
+		    {
+		      tid = i;
+		      break;
+		    }
+		}
+	      if (tid == -1)
+		continue;
+	    }
+	  /* At this point, TID is valid for the tunable we want.  See
+	     if the parsed type matches the desired type.  */
+
+	  if (tunable_list[tid].type.type_code == TUNABLE_TYPE_STRING)
+	    {
+	      /* This is a memory leak but there's no easy way around
+		 it, as the mapping will go away if the disk file is
+		 updated and the cache is reloaded.  */
+	      tunable_list[tid].val.strval.str = __strdup (value);
+	      tunable_list[tid].val.strval.len = strlen (value);
+	      tunable_list[tid].initialized = true;
+	    }
+	  else
+	    {
+	      tunable_val_t tval;
+	      if (tec->flags & TUNCONF_FLAG_PARSED)
+		{
+		  tval.numval = tec->parsed_value;
+		  do_tunable_update_val (& tunable_list[tid],
+					 &tval, NULL, NULL);
+		}
+	      else
+		{
+		  tunable_initialize (& tunable_list[tid],
+				      value, strlen (value));
+		}
+	    }
+	}
+    }
+#endif /* defined(SHARED) && defined (USE_LDCONFIG) */
+
   /* Ignore tunables for AT_SECURE programs.  */
   if (__libc_enable_secure)
     return;
diff --git a/elf/tunconf.h b/elf/tunconf.h
index b446211f94..81c0dd6b06 100644
--- a/elf/tunconf.h
+++ b/elf/tunconf.h
@@ -37,3 +37,6 @@ void parse_tunconf (const char *filename, char *opt_chroot);
 struct tunable_header_cached * get_tunconf_ext (uint32_t str_offset);
 #define TUNCONF_SIZE(thc_p) (sizeof(struct tunable_header_cached)		\
 		     + thc_p->num_tunables * sizeof (struct tunable_entry_cached))
+
+extern const struct tunable_header_cached *
+_dl_load_cache_tunables (const char **data);