about summary refs log tree commit diff
path: root/elf/dl-tunables.c
diff options
context:
space:
mode:
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>2023-12-06 10:24:01 -0300
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>2023-12-19 13:25:45 -0300
commit2a969b53c0b02fed7e43473a92f219d737fd217a (patch)
treeb55eda5dc496c260e9757a5fc3856838d85b38fd /elf/dl-tunables.c
parent5275fc784c8113c84c85ca028ce621f68fe6642b (diff)
downloadglibc-2a969b53c0b02fed7e43473a92f219d737fd217a.tar.gz
glibc-2a969b53c0b02fed7e43473a92f219d737fd217a.tar.xz
glibc-2a969b53c0b02fed7e43473a92f219d737fd217a.zip
elf: Do not duplicate the GLIBC_TUNABLES string
The tunable parsing duplicates the tunable environment variable so it
null-terminates each one since it simplifies the later parsing. It has
the drawback of adding another point of failure (__minimal_malloc
failing), and the memory copy requires tuning the compiler to avoid mem
operations calls.

The parsing now tracks the tunable start and its size. The
dl-tunable-parse.h adds helper functions to help parsing, like a strcmp
that also checks for size and an iterator for suboptions that are
comma-separated (used on hwcap parsing by x86, powerpc, and s390x).

Since the environment variable is allocated on the stack by the kernel,
it is safe to keep the references to the suboptions for later parsing
of string tunables (as done by set_hwcaps by multiple architectures).

Checked on x86_64-linux-gnu, powerpc64le-linux-gnu, and
aarch64-linux-gnu.
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
Diffstat (limited to 'elf/dl-tunables.c')
-rw-r--r--elf/dl-tunables.c80
1 files changed, 31 insertions, 49 deletions
diff --git a/elf/dl-tunables.c b/elf/dl-tunables.c
index 644d21d1b0..3d41e8e28e 100644
--- a/elf/dl-tunables.c
+++ b/elf/dl-tunables.c
@@ -36,31 +36,8 @@
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
 
-#include <not-errno.h>
-
-static char *
-tunables_strdup (const char *in)
-{
-  size_t i = 0;
-
-  while (in[i++] != '\0');
-  char *out = __minimal_malloc (i + 1);
-
-  /* For most of the tunables code, we ignore user errors.  However,
-     this is a system error - and running out of memory at program
-     startup should be reported, so we do.  */
-  if (out == NULL)
-    _dl_fatal_printf ("failed to allocate memory to process tunables\n");
-
-  while (i-- > 0)
-    out[i] = in[i];
-
-  return out;
-}
-
 static char **
-get_next_env (char **envp, char **name, size_t *namelen, char **val,
-	      char ***prev_envp)
+get_next_env (char **envp, char **name, char **val, char ***prev_envp)
 {
   while (envp != NULL && *envp != NULL)
     {
@@ -76,7 +53,6 @@ get_next_env (char **envp, char **name, size_t *namelen, char **val,
 	continue;
 
       *name = envline;
-      *namelen = len;
       *val = &envline[len + 1];
       *prev_envp = prev;
 
@@ -134,14 +110,14 @@ do_tunable_update_val (tunable_t *cur, const tunable_val_t *valp,
 /* Validate range of the input value and initialize the tunable CUR if it looks
    good.  */
 static void
-tunable_initialize (tunable_t *cur, const char *strval)
+tunable_initialize (tunable_t *cur, const char *strval, size_t len)
 {
-  tunable_val_t val;
+  tunable_val_t val = { 0 };
 
   if (cur->type.type_code != TUNABLE_TYPE_STRING)
     val.numval = (tunable_num_t) _dl_strtoul (strval, NULL);
   else
-    val.strval = strval;
+    val.strval = (struct tunable_str_t) { strval, len };
   do_tunable_update_val (cur, &val, NULL, NULL);
 }
 
@@ -165,29 +141,29 @@ struct tunable_toset_t
 {
   tunable_t *t;
   const char *value;
+  size_t len;
 };
 
 enum { tunables_list_size = array_length (tunable_list) };
 
 /* Parse the tunable string VALSTRING and set TUNABLES with the found tunables
-   and their respective strings.  VALSTRING is a duplicated values,  where
-   delimiters ':' are replaced with '\0', so string tunables are null
-   terminated.
+   and their respective values.  The VALSTRING is parsed in place, with the
+   tunable start and size recorded in TUNABLES.
    Return the number of tunables found (including 0 if the string is empty)
    or -1 if for an ill-formatted definition.  */
 static int
-parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
+parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
 {
   if (valstring == NULL || *valstring == '\0')
     return 0;
 
-  char *p = valstring;
+  const char *p = valstring;
   bool done = false;
   int ntunables = 0;
 
   while (!done)
     {
-      char *name = p;
+      const char *name = p;
 
       /* First, find where the name ends.  */
       while (*p != '=' && *p != ':' && *p != '\0')
@@ -209,7 +185,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
       /* Skip the '='.  */
       p++;
 
-      char *value = p;
+      const char *value = p;
 
       while (*p != '=' && *p != ':' && *p != '\0')
 	p++;
@@ -218,8 +194,6 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 	return -1;
       else if (*p == '\0')
 	done = true;
-      else
-	*p++ = '\0';
 
       /* Add the tunable if it exists.  */
       for (size_t i = 0; i < tunables_list_size; i++)
@@ -228,7 +202,8 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 
 	  if (tunable_is_name (cur->name, name))
 	    {
-	      tunables[ntunables++] = (struct tunable_toset_t) { cur, value };
+	      tunables[ntunables++] =
+		(struct tunable_toset_t) { cur, value, p - value };
 	      break;
 	    }
 	}
@@ -238,7 +213,7 @@ parse_tunables_string (char *valstring, struct tunable_toset_t *tunables)
 }
 
 static void
-parse_tunables (char *valstring)
+parse_tunables (const char *valstring)
 {
   struct tunable_toset_t tunables[tunables_list_size];
   int ntunables = parse_tunables_string (valstring, tunables);
@@ -250,7 +225,7 @@ parse_tunables (char *valstring)
     }
 
   for (int i = 0; i < ntunables; i++)
-    tunable_initialize (tunables[i].t, tunables[i].value);
+    tunable_initialize (tunables[i].t, tunables[i].value, tunables[i].len);
 }
 
 /* Initialize the tunables list from the environment.  For now we only use the
@@ -261,19 +236,20 @@ __tunables_init (char **envp)
 {
   char *envname = NULL;
   char *envval = NULL;
-  size_t len = 0;
   char **prev_envp = envp;
 
   /* Ignore tunables for AT_SECURE programs.  */
   if (__libc_enable_secure)
     return;
 
-  while ((envp = get_next_env (envp, &envname, &len, &envval,
-			       &prev_envp)) != NULL)
+  while ((envp = get_next_env (envp, &envname, &envval, &prev_envp)) != NULL)
     {
+      /* The environment variable is allocated on the stack by the kernel, so
+	 it is safe to keep the references to the suboptions for later parsing
+	 of string tunables.  */
       if (tunable_is_name ("GLIBC_TUNABLES", envname))
 	{
-	  parse_tunables (tunables_strdup (envval));
+	  parse_tunables (envval);
 	  continue;
 	}
 
@@ -291,7 +267,11 @@ __tunables_init (char **envp)
 	  /* We have a match.  Initialize and move on to the next line.  */
 	  if (tunable_is_name (name, envname))
 	    {
-	      tunable_initialize (cur, envval);
+	      size_t envvallen = 0;
+	      /* The environment variable is always null-terminated.  */
+	      for (const char *p = envval; *p != '\0'; p++, envvallen++);
+
+	      tunable_initialize (cur, envval, envvallen);
 	      break;
 	    }
 	}
@@ -305,7 +285,7 @@ __tunables_print (void)
     {
       const tunable_t *cur = &tunable_list[i];
       if (cur->type.type_code == TUNABLE_TYPE_STRING
-	  && cur->val.strval == NULL)
+	  && cur->val.strval.str == NULL)
 	_dl_printf ("%s:\n", cur->name);
       else
 	{
@@ -331,7 +311,9 @@ __tunables_print (void)
 			  (size_t) cur->type.max);
 	      break;
 	    case TUNABLE_TYPE_STRING:
-	      _dl_printf ("%s\n", cur->val.strval);
+	      _dl_printf ("%.*s\n",
+			  (int) cur->val.strval.len,
+			  cur->val.strval.str);
 	      break;
 	    default:
 	      __builtin_unreachable ();
@@ -364,7 +346,7 @@ __tunable_get_default (tunable_id_t id, void *valp)
 	}
     case TUNABLE_TYPE_STRING:
 	{
-	  *((const char **)valp) = cur->def.strval;
+	  *((const struct tunable_str_t **)valp) = &cur->def.strval;
 	  break;
 	}
     default:
@@ -399,7 +381,7 @@ __tunable_get_val (tunable_id_t id, void *valp, tunable_callback_t callback)
 	}
     case TUNABLE_TYPE_STRING:
 	{
-	  *((const char **)valp) = cur->val.strval;
+	  *((const struct tunable_str_t **) valp) = &cur->val.strval;
 	  break;
 	}
     default: