[3/3] Add system-wide tunables: Apply tunables part

Message ID xna5lq2er9.fsf@greed.delorie.com
State Changes Requested
Headers
Series [1/3] Add system-wide tunables: ldconfig part |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_glibc_build--master-aarch64 success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-aarch64 fail Testing failed
linaro-tcwg-bot/tcwg_glibc_build--master-arm success Testing passed
linaro-tcwg-bot/tcwg_glibc_check--master-arm success Testing passed
redhat-pt-bot/TryBot-apply_patch success Patch applied to master at the time it was sent
redhat-pt-bot/TryBot-32bit fail Patch caused testsuite regressions

Commit Message

DJ Delorie April 19, 2024, 3:49 a.m. UTC
  Load ld.so.cache and fetch the tunables extension.  Apply
those tunables to the current program.  We do not yet apply
security policies.
---
 elf/dl-cache.c    | 24 +++++++++++++
 elf/dl-tunables.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++
 elf/tunconf.h     |  3 ++
 3 files changed, 112 insertions(+)
  

Comments

Adhemerval Zanella May 29, 2024, 1:18 p.m. UTC | #1
On 19/04/24 00:49, DJ Delorie wrote:
> 
> Load ld.so.cache and fetch the tunables extension.  Apply
> those tunables to the current program.  We do not yet apply
> security policies.

So the idea is to have GLIBC_TUNABLES to override system-wide tunables
and later handle if the tunable can be override or not?

> ---
>  elf/dl-cache.c    | 24 +++++++++++++
>  elf/dl-tunables.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++
>  elf/tunconf.h     |  3 ++
>  3 files changed, 112 insertions(+)
> 
> diff --git a/elf/dl-cache.c b/elf/dl-cache.c
> index d90278889d..7ccb96d1ca 100644
> --- a/elf/dl-cache.c
> +++ b/elf/dl-cache.c
> @@ -28,6 +28,7 @@
>  #include <dl-isa-level.h>
>  #include <sys/types.h>
>  #include <sys/stat.h>
> +#include "tunconf.h"
>  
>  #ifndef _DL_PLATFORMS_COUNT
>  # define _DL_PLATFORMS_COUNT 0
> @@ -616,3 +617,26 @@ _dl_unload_cache (void)
>       now.  */
>  }
>  #endif
> +
> +const struct tunable_header_cached *
> +_dl_load_cache_tunables (const char **data)
> +{
> +  struct cache_extension_all_loaded ext;
> +
> +  /* This loads the cache (temporary).  */
> +  if (_dl_check_ldsocache_needs_loading ())
> +    _dl_maybe_load_ldsocache ();
> +
> +  if (cache_new && cache_new != (void *) -1)
> +    *data = (const char *) cache_new;
> +  else
> +    *data =  (const char *) &cache->libs[cache->nlibs];
> +
> +  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;
> +  return ext.sections[cache_extension_tag_tunables].base;
> +}
> diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
> index d3ccd2ecd4..5ecddccff6 100644
> --- a/elf/dl-tunables.c
> +++ b/elf/dl-tunables.c
> @@ -35,6 +35,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)
> @@ -264,6 +265,31 @@ parse_tunables (const char *valstring)
>  		       tunables[i].t->name);
>  }
>  
> +/* We need this functionality before we load the real strcmp function.  */
> +static int
> +dl_strcmp(const char *s1, const char *s2)
> +{
> +  while (*s1 && *s2)
> +    {
> +      if (*s1 != *s2)
> +	return *s1 - *s2;
> +      ++s1;
> +      ++s2;
> +    }
> +  return *s1 - *s2;
> +}
> +static int
> +dl_strlen(const char *s)
> +{
> +  int len = 0;
> +  while (*s)
> +    {
> +      ++s;
> +      ++len;
> +    }
> +  return len;
> +}
> +
>  /* Initialize the tunables list from the environment.  For now we only use the
>     ENV_ALIAS to find values.  Later we will also use the tunable names to find
>     values.  */
> @@ -273,6 +299,65 @@ __tunables_init (char **envp)
>    char *envname = NULL;
>    char *envval = NULL;
>    char **prev_envp = envp;
> +  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
> +	      || dl_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 (dl_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.  */
> +	      tunable_list[tid].val.strval.str = __strdup (value);
> +	      tunable_list[tid].val.strval.len = strlen (value);
> +	    }
> +	  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, dl_strlen(value));
> +		}
> +	    }
> +	}
> +    }
>  
>    /* Ignore tunables for AT_SECURE programs.  */
>    if (__libc_enable_secure)
> diff --git a/elf/tunconf.h b/elf/tunconf.h
> index a6c5f0dd9a..623fb7546d 100644
> --- a/elf/tunconf.h
> +++ b/elf/tunconf.h
> @@ -38,3 +38,6 @@ void parse_tunconf (const char *filename, int do_chroot, 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);
  
DJ Delorie May 29, 2024, 5:19 p.m. UTC | #2
Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> writes:
> On 19/04/24 00:49, DJ Delorie wrote:
>> 
>> Load ld.so.cache and fetch the tunables extension.  Apply
>> those tunables to the current program.  We do not yet apply
>> security policies.
>
> So the idea is to have GLIBC_TUNABLES to override system-wide tunables
> and later handle if the tunable can be override or not?

At the time GLIBC_TUNABLES is read, each tunable is either applied or
not depending on the policy.  It will happen at startup, not "as tunable
values are used".  This is similar to other security policies - tunables
are either parsed or not, but the code that uses the tunables values
doesn't have security code in it.
  
Adhemerval Zanella June 19, 2024, 12:53 p.m. UTC | #3
On 29/05/24 14:19, DJ Delorie wrote:
> Adhemerval Zanella Netto <adhemerval.zanella@linaro.org> writes:
>> On 19/04/24 00:49, DJ Delorie wrote:
>>>
>>> Load ld.so.cache and fetch the tunables extension.  Apply
>>> those tunables to the current program.  We do not yet apply
>>> security policies.
>>
>> So the idea is to have GLIBC_TUNABLES to override system-wide tunables
>> and later handle if the tunable can be override or not?
> 
> At the time GLIBC_TUNABLES is read, each tunable is either applied or
> not depending on the policy.  It will happen at startup, not "as tunable
> values are used".  This is similar to other security policies - tunables
> are either parsed or not, but the code that uses the tunables values
> doesn't have security code in it.
> 

So the idea would that on GLIBC_TUNABLES parsing, if a system-wide option
is already set it would depend whether the policy allow overriding? Or
would any GLIBC_TUNABLE would be ignore if a system-wide option is set?
  

Patch

diff --git a/elf/dl-cache.c b/elf/dl-cache.c
index d90278889d..7ccb96d1ca 100644
--- a/elf/dl-cache.c
+++ b/elf/dl-cache.c
@@ -28,6 +28,7 @@ 
 #include <dl-isa-level.h>
 #include <sys/types.h>
 #include <sys/stat.h>
+#include "tunconf.h"
 
 #ifndef _DL_PLATFORMS_COUNT
 # define _DL_PLATFORMS_COUNT 0
@@ -616,3 +617,26 @@  _dl_unload_cache (void)
      now.  */
 }
 #endif
+
+const struct tunable_header_cached *
+_dl_load_cache_tunables (const char **data)
+{
+  struct cache_extension_all_loaded ext;
+
+  /* This loads the cache (temporary).  */
+  if (_dl_check_ldsocache_needs_loading ())
+    _dl_maybe_load_ldsocache ();
+
+  if (cache_new && cache_new != (void *) -1)
+    *data = (const char *) cache_new;
+  else
+    *data =  (const char *) &cache->libs[cache->nlibs];
+
+  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;
+  return ext.sections[cache_extension_tag_tunables].base;
+}
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index d3ccd2ecd4..5ecddccff6 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -35,6 +35,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)
@@ -264,6 +265,31 @@  parse_tunables (const char *valstring)
 		       tunables[i].t->name);
 }
 
+/* We need this functionality before we load the real strcmp function.  */
+static int
+dl_strcmp(const char *s1, const char *s2)
+{
+  while (*s1 && *s2)
+    {
+      if (*s1 != *s2)
+	return *s1 - *s2;
+      ++s1;
+      ++s2;
+    }
+  return *s1 - *s2;
+}
+static int
+dl_strlen(const char *s)
+{
+  int len = 0;
+  while (*s)
+    {
+      ++s;
+      ++len;
+    }
+  return len;
+}
+
 /* Initialize the tunables list from the environment.  For now we only use the
    ENV_ALIAS to find values.  Later we will also use the tunable names to find
    values.  */
@@ -273,6 +299,65 @@  __tunables_init (char **envp)
   char *envname = NULL;
   char *envval = NULL;
   char **prev_envp = envp;
+  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
+	      || dl_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 (dl_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.  */
+	      tunable_list[tid].val.strval.str = __strdup (value);
+	      tunable_list[tid].val.strval.len = strlen (value);
+	    }
+	  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, dl_strlen(value));
+		}
+	    }
+	}
+    }
 
   /* Ignore tunables for AT_SECURE programs.  */
   if (__libc_enable_secure)
diff --git a/elf/tunconf.h b/elf/tunconf.h
index a6c5f0dd9a..623fb7546d 100644
--- a/elf/tunconf.h
+++ b/elf/tunconf.h
@@ -38,3 +38,6 @@  void parse_tunconf (const char *filename, int do_chroot, 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);