]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
elf: Only process multiple tunable once (BZ 31686)
authorAdhemerval Zanella <adhemerval.zanella@linaro.org>
Mon, 6 May 2024 16:18:45 +0000 (13:18 -0300)
committerAdhemerval Zanella <adhemerval.zanella@linaro.org>
Tue, 7 May 2024 15:16:36 +0000 (12:16 -0300)
The 680c597e9c3 commit made loader reject ill-formatted strings by
first tracking all set tunables and then applying them. However, it does
not take into consideration if the same tunable is set multiple times,
where parse_tunables_string appends the found tunable without checking
if it was already in the list. It leads to a stack-based buffer overflow
if the tunable is specified more than the total number of tunables.  For
instance:

  GLIBC_TUNABLES=glibc.malloc.check=2:... (repeat over the number of
  total support for different tunable).

Instead, use the index of the tunable list to get the expected tunable
entry.  Since now the initial list is zero-initialized, the compiler
might emit an extra memset and this requires some minor adjustment
on some ports.

Checked on x86_64-linux-gnu and aarch64-linux-gnu.

Reported-by: Yuto Maeda <maeda@cyberdefense.jp>
Reported-by: Yutaro Shimizu <shimizu@cyberdefense.jp>
Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
elf/dl-tunables.c
elf/tst-tunables.c
sysdeps/aarch64/multiarch/memset_generic.S
sysdeps/sparc/sparc64/rtld-memset.c

index d3ccd2ecd4e2e1cdc4f6a9ac55637afda97ab97a..1db80e0f924047eaa2290f14a723929ecd0158ce 100644 (file)
@@ -32,6 +32,7 @@
 #include <ldsodefs.h>
 #include <array_length.h>
 #include <dl-minimal-malloc.h>
+#include <dl-symbol-redir-ifunc.h>
 
 #define TUNABLES_INTERNAL 1
 #include "dl-tunables.h"
@@ -221,8 +222,7 @@ parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
 
          if (tunable_is_name (cur->name, name))
            {
-             tunables[ntunables++] =
-               (struct tunable_toset_t) { cur, value, p - value };
+             tunables[i] = (struct tunable_toset_t) { cur, value, p - value };
 
              /* Ignore tunables if enable_secure is set */
              if (tunable_is_name ("glibc.rtld.enable_secure", name))
@@ -245,23 +245,27 @@ parse_tunables_string (const char *valstring, struct tunable_toset_t *tunables)
 static void
 parse_tunables (const char *valstring)
 {
-  struct tunable_toset_t tunables[tunables_list_size];
-  int ntunables = parse_tunables_string (valstring, tunables);
-  if (ntunables == -1)
+  struct tunable_toset_t tunables[tunables_list_size] = { 0 };
+  if (parse_tunables_string (valstring, tunables) == -1)
     {
       _dl_error_printf (
         "WARNING: ld.so: invalid GLIBC_TUNABLES `%s': ignored.\n", valstring);
       return;
     }
 
-  for (int i = 0; i < ntunables; i++)
-    if (!tunable_initialize (tunables[i].t, tunables[i].value,
-                            tunables[i].len))
-      _dl_error_printf ("WARNING: ld.so: invalid GLIBC_TUNABLES value `%.*s' "
-                      "for option `%s': ignored.\n",
-                      (int) tunables[i].len,
-                      tunables[i].value,
-                      tunables[i].t->name);
+  for (int i = 0; i < tunables_list_size; i++)
+    {
+      if (tunables[i].t == NULL)
+       continue;
+
+      if (!tunable_initialize (tunables[i].t, tunables[i].value,
+                              tunables[i].len))
+       _dl_error_printf ("WARNING: ld.so: invalid GLIBC_TUNABLES value `%.*s' "
+                         "for option `%s': ignored.\n",
+                         (int) tunables[i].len,
+                         tunables[i].value,
+                         tunables[i].t->name);
+    }
 }
 
 /* Initialize the tunables list from the environment.  For now we only use the
index 095b5c81d95c87605b1bc11a53107926be656103..dff34ed748b4ae83974c188b0a5fdb6dbccb4274 100644 (file)
    <https://www.gnu.org/licenses/>.  */
 
 #include <array_length.h>
+/* The test uses the tunable_list size, which is only exported for
+   ld.so.  This will result in a copy of tunable_list, which is ununsed by
+   the test itself.  */
+#define TUNABLES_INTERNAL 1
 #include <dl-tunables.h>
 #include <getopt.h>
 #include <intprops.h>
 #include <stdlib.h>
 #include <support/capture_subprocess.h>
 #include <support/check.h>
+#include <support/support.h>
 
 static int restart;
 #define CMDLINE_OPTIONS \
   { "restart", no_argument, &restart, 1 },
 
-static const struct test_t
+static struct test_t
 {
   const char *name;
   const char *value;
@@ -284,6 +289,29 @@ static const struct test_t
     0,
     0,
   },
+  /* Also check for repeated tunables with a count larger than the total number
+     of tunables.  */
+  {
+    "GLIBC_TUNABLES",
+    NULL,
+    2,
+    0,
+    0,
+  },
+  {
+    "GLIBC_TUNABLES",
+    NULL,
+    1,
+    0,
+    0,
+  },
+  {
+    "GLIBC_TUNABLES",
+    NULL,
+    0,
+    0,
+    0,
+  },
 };
 
 static int
@@ -327,6 +355,37 @@ do_test (int argc, char *argv[])
     spargv[i] = NULL;
   }
 
+  /* Create a tunable line with the duplicate values with a total number
+     larger than the different number of tunables.  */
+  {
+    enum { tunables_list_size = array_length (tunable_list) };
+    const char *value = "";
+    for (int i = 0; i < tunables_list_size; i++)
+      value = xasprintf ("%sglibc.malloc.check=2%c",
+                        value,
+                        i == (tunables_list_size - 1) ? '\0' : ':');
+    tests[33].value = value;
+  }
+  /* Same as before, but the last tunable values is differen than the
+     rest.  */
+  {
+    enum { tunables_list_size = array_length (tunable_list) };
+    const char *value = "";
+    for (int i = 0; i < tunables_list_size - 1; i++)
+      value = xasprintf ("%sglibc.malloc.check=2:", value);
+    value = xasprintf ("%sglibc.malloc.check=1", value);
+    tests[34].value = value;
+  }
+  /* Same as before, but with an invalid last entry.  */
+  {
+    enum { tunables_list_size = array_length (tunable_list) };
+    const char *value = "";
+    for (int i = 0; i < tunables_list_size - 1; i++)
+      value = xasprintf ("%sglibc.malloc.check=2:", value);
+    value = xasprintf ("%sglibc.malloc.check=1=1", value);
+    tests[35].value = value;
+  }
+
   for (int i = 0; i < array_length (tests); i++)
     {
       snprintf (nteststr, sizeof nteststr, "%d", i);
index 81748bdbce53e636f92822c39a6d45b1f4619fa6..e125a5ed853301e1299b9998cc32bfeb445dfe5f 100644 (file)
@@ -33,3 +33,7 @@
 #endif
 
 #include <../memset.S>
+
+#if IS_IN (rtld)
+strong_alias (memset, __memset_generic)
+#endif
index 55f38357902933b57b6b06735871de9ba1db014c..a19202a620fb8f7d750e36f35a663f0ddf0220fd 100644 (file)
@@ -1 +1,4 @@
 #include <string/memset.c>
+#if IS_IN(rtld)
+strong_alias (memset, __memset_ultra1)
+#endif