[v2] elf/rtld: Fix auxiliary vector for enable_secure

Message ID 20240702132520.941809-1-stli@linux.ibm.com
State Committed
Commit d2f6ceaccbae2f645075dedad2b762896da1ec04
Headers
Series [v2] elf/rtld: Fix auxiliary vector for enable_secure |

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_check--master-arm success Test 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

Stefan Liebler July 2, 2024, 1:25 p.m. UTC
  Starting with commit
59974938fe1f4add843f5325f78e2a7ccd8db853
elf/rtld: Count skipped environment variables for enable_secure

The new testcase elf/tst-tunables-enable_secure-env segfaults on s390 (31bit).
There _start parses the auxiliary vector for some additional checks.

Therefore it skips over the zeros after the environment variables ...
0x7fffac20:     0x7fffbd17      0x7fffbd32      0x7fffbd69      0x00000000
------------------------------------------------^^^last environment variable

... and then it parses the auxiliary vector and stops at AT_NULL.
0x7fffac30:     0x00000000      0x00000021      0x00000000      0x00000000
--------------------------------^^^AT_SYSINFO_EHDR--------------^^^AT_NULL
----------------^^^newp-----------------------------------------^^^oldp
Afterwards it tries to access AT_PHDR which points to somewhere and segfaults.

Due to not incorporating the skip_env variable in the computation of oldp
when shuffling down the auxv in rtld.c, it just copies one entry with AT_NULL
and value 0x00000021 and stops the loop.  In reality we have skipped
GLIBC_TUNABLES environment variable (=> skip_env=1). Thus we should copy from
here:
0x7fffac40:     0x00000021      0x7ffff000      0x00000010      0x007fffff
----------------^^^fixed-oldp

This patch fixes the computation of oldp when shuffling down auxiliary vector.
It also adds some checks in the testcase.  Those checks also fail on
s390x (64bit) and x86_64 without the fix.

Co-authored-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
---
 elf/Makefile                         |   9 +-
 elf/rtld.c                           |   2 +-
 elf/tst-tunables-enable_secure-env.c | 127 ++++++++++++++++++++++++++-
 3 files changed, 126 insertions(+), 12 deletions(-)
  

Comments

Adhemerval Zanella Netto July 2, 2024, 4:57 p.m. UTC | #1
On 02/07/24 10:25, Stefan Liebler wrote:
> Starting with commit
> 59974938fe1f4add843f5325f78e2a7ccd8db853
> elf/rtld: Count skipped environment variables for enable_secure
> 
> The new testcase elf/tst-tunables-enable_secure-env segfaults on s390 (31bit).
> There _start parses the auxiliary vector for some additional checks.
> 
> Therefore it skips over the zeros after the environment variables ...
> 0x7fffac20:     0x7fffbd17      0x7fffbd32      0x7fffbd69      0x00000000
> ------------------------------------------------^^^last environment variable
> 
> ... and then it parses the auxiliary vector and stops at AT_NULL.
> 0x7fffac30:     0x00000000      0x00000021      0x00000000      0x00000000
> --------------------------------^^^AT_SYSINFO_EHDR--------------^^^AT_NULL
> ----------------^^^newp-----------------------------------------^^^oldp
> Afterwards it tries to access AT_PHDR which points to somewhere and segfaults.
> 
> Due to not incorporating the skip_env variable in the computation of oldp
> when shuffling down the auxv in rtld.c, it just copies one entry with AT_NULL
> and value 0x00000021 and stops the loop.  In reality we have skipped
> GLIBC_TUNABLES environment variable (=> skip_env=1). Thus we should copy from
> here:
> 0x7fffac40:     0x00000021      0x7ffff000      0x00000010      0x007fffff
> ----------------^^^fixed-oldp
> 
> This patch fixes the computation of oldp when shuffling down auxiliary vector.
> It also adds some checks in the testcase.  Those checks also fail on
> s390x (64bit) and x86_64 without the fix.
> 
> Co-authored-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>

LGTM, thanks.

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

> ---
>  elf/Makefile                         |   9 +-
>  elf/rtld.c                           |   2 +-
>  elf/tst-tunables-enable_secure-env.c | 127 ++++++++++++++++++++++++++-
>  3 files changed, 126 insertions(+), 12 deletions(-)
> 
> diff --git a/elf/Makefile b/elf/Makefile
> index 24ad5221c2..a3475f3fb5 100644
> --- a/elf/Makefile
> +++ b/elf/Makefile
> @@ -1224,7 +1224,6 @@ tests-special += \
>    $(objpfx)tst-trace3.out \
>    $(objpfx)tst-trace4.out \
>    $(objpfx)tst-trace5.out \
> -  $(objpfx)tst-tunables-enable_secure-env.out \
>    $(objpfx)tst-unused-dep-cmp.out \
>    $(objpfx)tst-unused-dep.out \
>    # tests-special
> @@ -2252,13 +2251,7 @@ $(objpfx)tst-unused-dep-cmp.out: $(objpfx)tst-unused-dep.out
>  	cmp $< /dev/null > $@; \
>  	$(evaluate-test)
>  
> -$(objpfx)tst-tunables-enable_secure-env.out: $(objpfx)tst-tunables-enable_secure-env
> -	$(test-wrapper-env) \
> -	GLIBC_TUNABLES=glibc.rtld.enable_secure=1 \
> -	$(rtld-prefix) \
> -	  $< > $@; \
> -	$(evaluate-test)
> -
> +tst-tunables-enable_secure-env-ARGS = -- $(host-test-program-cmd)
>  
>  $(objpfx)tst-audit11.out: $(objpfx)tst-auditmod11.so $(objpfx)tst-audit11mod1.so
>  tst-audit11-ENV = LD_AUDIT=$(objpfx)tst-auditmod11.so
> diff --git a/elf/rtld.c b/elf/rtld.c
> index 6352ba76c5..bfdf632e77 100644
> --- a/elf/rtld.c
> +++ b/elf/rtld.c
> @@ -1327,7 +1327,7 @@ _dl_start_args_adjust (int skip_args, int skip_env)
>  
>    /* Shuffle auxv down. */
>    ElfW(auxv_t) ax;
> -  char *oldp = (char *) (p + 1);
> +  char *oldp = (char *) (p + 1 + skip_env);
>    char *newp = (char *) (sp + 1);
>    do
>      {
> diff --git a/elf/tst-tunables-enable_secure-env.c b/elf/tst-tunables-enable_secure-env.c
> index 24e846f299..01f121efc3 100644
> --- a/elf/tst-tunables-enable_secure-env.c
> +++ b/elf/tst-tunables-enable_secure-env.c
> @@ -17,15 +17,136 @@
>     License along with the GNU C Library; if not, see
>     <https://www.gnu.org/licenses/>.  */
>  
> +#include <array_length.h>
> +#include <errno.h>
> +#include <getopt.h>
> +#include <intprops.h>
> +#include <stdlib.h>
>  #include <support/capture_subprocess.h>
>  #include <support/check.h>
> +#ifdef __linux__
> +# define HAVE_AUXV 1
> +# include <sys/auxv.h>
> +#else
> +# define HAVE_AUXV 0
> +#endif
> +
> +/* Nonzero if the program gets called via `exec'.  */
> +#define CMDLINE_OPTIONS \
> +  { "restart", no_argument, &restart, 1 },
> +static int restart;
> +
> +/* Hold the four initial argument used to respawn the process, plus the extra
> +   '--direct', '--restart', auxiliary vector values, and final NULL.  */
> +static char *spargs[11];
> +
> +#if HAVE_AUXV
> +static void
> +check_auxv (unsigned long type, char *argv)
> +{
> +  char *endptr;
> +  errno = 0;
> +  unsigned long int varg = strtol (argv, &endptr, 10);
> +  TEST_VERIFY_EXIT (errno == 0);
> +  TEST_VERIFY_EXIT (*endptr == '\0');
> +  errno = 0;
> +  unsigned long int v = getauxval (type);
> +  TEST_COMPARE (errno, 0);
> +  TEST_COMPARE (varg, v);
> +}
> +#endif
> +
> +/* Called on process re-execution.  */
> +_Noreturn static void
> +handle_restart (int argc, char *argv[])
> +{
> +  TEST_VERIFY (getenv ("GLIBC_TUNABLES") == NULL);
> +  TEST_VERIFY (getenv ("LD_BIND_NOW") == NULL);
> +
> +#if HAVE_AUXV
> +  TEST_VERIFY_EXIT (argc == 4);
> +  check_auxv (AT_PHENT, argv[0]);
> +  check_auxv (AT_PHNUM, argv[1]);
> +  check_auxv (AT_PAGESZ, argv[2]);
> +  check_auxv (AT_HWCAP, argv[3]);
> +#endif
> +
> +  exit (EXIT_SUCCESS);
> +}
>  
>  static int
>  do_test (int argc, char *argv[])
>  {
> -  /* Ensure that no assertions are hit when a dynamically linked application
> -     runs.  This test requires that GLIBC_TUNABLES=glibc.rtld.enable_secure=1
> -     is set. */
> +  /* We must have either:
> +
> +     - four parameter if called initially:
> +       + path for ld.so            [optional]
> +       + "--library-path"          [optional]
> +       + the library path          [optional]
> +       + the application name
> +
> +     - either parameters left if called through re-execution.
> +       + auxiliary vector value 1
> +       + auxiliary vector value 2
> +       + auxiliary vector value 3
> +       + auxiliary vector value 4
> +  */
> +  if (restart)
> +    handle_restart (argc - 1, &argv[1]);
> +
> +  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
> +
> +#if HAVE_AUXV
> +  struct
> +  {
> +    unsigned long int type;
> +    char str[INT_BUFSIZE_BOUND (unsigned long)];
> +  } auxvals[] =
> +  {
> +    /* Check some auxiliary values that should be constant over process
> +       re-execution.  */
> +    { AT_PHENT },
> +    { AT_PHNUM },
> +    { AT_PAGESZ },
> +    { AT_HWCAP },
> +  };
> +  for (int i = 0; i < array_length (auxvals); i++)
> +  {
> +    unsigned long int v = getauxval (auxvals[i].type);
> +    snprintf (auxvals[i].str, sizeof auxvals[i].str, "%lu", v);
> +  }
> +#endif
> +
> +  {
> +    int i;
> +    for (i = 0; i < argc - 1; i++)
> +      spargs[i] = argv[i + 1];
> +    spargs[i++] = (char *) "--direct";
> +    spargs[i++] = (char *) "--restart";
> +#if HAVE_AUXV
> +    for (int j = 0; j < array_length (auxvals); j++)
> +      spargs[i++] = auxvals[j].str;
> +#endif
> +    spargs[i] = NULL;
> +  }
> +
> +  {
> +    char *envs[] =
> +    {
> +      /* Add some environment variable that should be filtered out.  */
> +      (char *) "GLIBC_TUNABLES=glibc.rtld.enable_secure=1",
> +      (char* ) "LD_BIND_NOW=0",
> +      NULL,
> +    };
> +    struct support_capture_subprocess result
> +      = support_capture_subprogram (spargs[0], spargs, envs);
> +    support_capture_subprocess_check (&result,
> +				      "tst-tunables-enable_secure-env",
> +				      0,
> +				      sc_allow_none);
> +    support_capture_subprocess_free (&result);
> +  }
> +
>    return 0;
>  }
>
  
Stefan Liebler July 3, 2024, 11:06 a.m. UTC | #2
On 02.07.24 18:57, Adhemerval Zanella Netto wrote:
> 
> 
> On 02/07/24 10:25, Stefan Liebler wrote:
>> Starting with commit
>> 59974938fe1f4add843f5325f78e2a7ccd8db853
>> elf/rtld: Count skipped environment variables for enable_secure
>>
>> The new testcase elf/tst-tunables-enable_secure-env segfaults on s390 (31bit).
>> There _start parses the auxiliary vector for some additional checks.
>>
>> Therefore it skips over the zeros after the environment variables ...
>> 0x7fffac20:     0x7fffbd17      0x7fffbd32      0x7fffbd69      0x00000000
>> ------------------------------------------------^^^last environment variable
>>
>> ... and then it parses the auxiliary vector and stops at AT_NULL.
>> 0x7fffac30:     0x00000000      0x00000021      0x00000000      0x00000000
>> --------------------------------^^^AT_SYSINFO_EHDR--------------^^^AT_NULL
>> ----------------^^^newp-----------------------------------------^^^oldp
>> Afterwards it tries to access AT_PHDR which points to somewhere and segfaults.
>>
>> Due to not incorporating the skip_env variable in the computation of oldp
>> when shuffling down the auxv in rtld.c, it just copies one entry with AT_NULL
>> and value 0x00000021 and stops the loop.  In reality we have skipped
>> GLIBC_TUNABLES environment variable (=> skip_env=1). Thus we should copy from
>> here:
>> 0x7fffac40:     0x00000021      0x7ffff000      0x00000010      0x007fffff
>> ----------------^^^fixed-oldp
>>
>> This patch fixes the computation of oldp when shuffling down auxiliary vector.
>> It also adds some checks in the testcase.  Those checks also fail on
>> s390x (64bit) and x86_64 without the fix.
>>
>> Co-authored-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
> 
> LGTM, thanks.
> 
> Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
> 
I've just committed this patch. Thanks, Stefan.
  

Patch

diff --git a/elf/Makefile b/elf/Makefile
index 24ad5221c2..a3475f3fb5 100644
--- a/elf/Makefile
+++ b/elf/Makefile
@@ -1224,7 +1224,6 @@  tests-special += \
   $(objpfx)tst-trace3.out \
   $(objpfx)tst-trace4.out \
   $(objpfx)tst-trace5.out \
-  $(objpfx)tst-tunables-enable_secure-env.out \
   $(objpfx)tst-unused-dep-cmp.out \
   $(objpfx)tst-unused-dep.out \
   # tests-special
@@ -2252,13 +2251,7 @@  $(objpfx)tst-unused-dep-cmp.out: $(objpfx)tst-unused-dep.out
 	cmp $< /dev/null > $@; \
 	$(evaluate-test)
 
-$(objpfx)tst-tunables-enable_secure-env.out: $(objpfx)tst-tunables-enable_secure-env
-	$(test-wrapper-env) \
-	GLIBC_TUNABLES=glibc.rtld.enable_secure=1 \
-	$(rtld-prefix) \
-	  $< > $@; \
-	$(evaluate-test)
-
+tst-tunables-enable_secure-env-ARGS = -- $(host-test-program-cmd)
 
 $(objpfx)tst-audit11.out: $(objpfx)tst-auditmod11.so $(objpfx)tst-audit11mod1.so
 tst-audit11-ENV = LD_AUDIT=$(objpfx)tst-auditmod11.so
diff --git a/elf/rtld.c b/elf/rtld.c
index 6352ba76c5..bfdf632e77 100644
--- a/elf/rtld.c
+++ b/elf/rtld.c
@@ -1327,7 +1327,7 @@  _dl_start_args_adjust (int skip_args, int skip_env)
 
   /* Shuffle auxv down. */
   ElfW(auxv_t) ax;
-  char *oldp = (char *) (p + 1);
+  char *oldp = (char *) (p + 1 + skip_env);
   char *newp = (char *) (sp + 1);
   do
     {
diff --git a/elf/tst-tunables-enable_secure-env.c b/elf/tst-tunables-enable_secure-env.c
index 24e846f299..01f121efc3 100644
--- a/elf/tst-tunables-enable_secure-env.c
+++ b/elf/tst-tunables-enable_secure-env.c
@@ -17,15 +17,136 @@ 
    License along with the GNU C Library; if not, see
    <https://www.gnu.org/licenses/>.  */
 
+#include <array_length.h>
+#include <errno.h>
+#include <getopt.h>
+#include <intprops.h>
+#include <stdlib.h>
 #include <support/capture_subprocess.h>
 #include <support/check.h>
+#ifdef __linux__
+# define HAVE_AUXV 1
+# include <sys/auxv.h>
+#else
+# define HAVE_AUXV 0
+#endif
+
+/* Nonzero if the program gets called via `exec'.  */
+#define CMDLINE_OPTIONS \
+  { "restart", no_argument, &restart, 1 },
+static int restart;
+
+/* Hold the four initial argument used to respawn the process, plus the extra
+   '--direct', '--restart', auxiliary vector values, and final NULL.  */
+static char *spargs[11];
+
+#if HAVE_AUXV
+static void
+check_auxv (unsigned long type, char *argv)
+{
+  char *endptr;
+  errno = 0;
+  unsigned long int varg = strtol (argv, &endptr, 10);
+  TEST_VERIFY_EXIT (errno == 0);
+  TEST_VERIFY_EXIT (*endptr == '\0');
+  errno = 0;
+  unsigned long int v = getauxval (type);
+  TEST_COMPARE (errno, 0);
+  TEST_COMPARE (varg, v);
+}
+#endif
+
+/* Called on process re-execution.  */
+_Noreturn static void
+handle_restart (int argc, char *argv[])
+{
+  TEST_VERIFY (getenv ("GLIBC_TUNABLES") == NULL);
+  TEST_VERIFY (getenv ("LD_BIND_NOW") == NULL);
+
+#if HAVE_AUXV
+  TEST_VERIFY_EXIT (argc == 4);
+  check_auxv (AT_PHENT, argv[0]);
+  check_auxv (AT_PHNUM, argv[1]);
+  check_auxv (AT_PAGESZ, argv[2]);
+  check_auxv (AT_HWCAP, argv[3]);
+#endif
+
+  exit (EXIT_SUCCESS);
+}
 
 static int
 do_test (int argc, char *argv[])
 {
-  /* Ensure that no assertions are hit when a dynamically linked application
-     runs.  This test requires that GLIBC_TUNABLES=glibc.rtld.enable_secure=1
-     is set. */
+  /* We must have either:
+
+     - four parameter if called initially:
+       + path for ld.so            [optional]
+       + "--library-path"          [optional]
+       + the library path          [optional]
+       + the application name
+
+     - either parameters left if called through re-execution.
+       + auxiliary vector value 1
+       + auxiliary vector value 2
+       + auxiliary vector value 3
+       + auxiliary vector value 4
+  */
+  if (restart)
+    handle_restart (argc - 1, &argv[1]);
+
+  TEST_VERIFY_EXIT (argc == 2 || argc == 5);
+
+#if HAVE_AUXV
+  struct
+  {
+    unsigned long int type;
+    char str[INT_BUFSIZE_BOUND (unsigned long)];
+  } auxvals[] =
+  {
+    /* Check some auxiliary values that should be constant over process
+       re-execution.  */
+    { AT_PHENT },
+    { AT_PHNUM },
+    { AT_PAGESZ },
+    { AT_HWCAP },
+  };
+  for (int i = 0; i < array_length (auxvals); i++)
+  {
+    unsigned long int v = getauxval (auxvals[i].type);
+    snprintf (auxvals[i].str, sizeof auxvals[i].str, "%lu", v);
+  }
+#endif
+
+  {
+    int i;
+    for (i = 0; i < argc - 1; i++)
+      spargs[i] = argv[i + 1];
+    spargs[i++] = (char *) "--direct";
+    spargs[i++] = (char *) "--restart";
+#if HAVE_AUXV
+    for (int j = 0; j < array_length (auxvals); j++)
+      spargs[i++] = auxvals[j].str;
+#endif
+    spargs[i] = NULL;
+  }
+
+  {
+    char *envs[] =
+    {
+      /* Add some environment variable that should be filtered out.  */
+      (char *) "GLIBC_TUNABLES=glibc.rtld.enable_secure=1",
+      (char* ) "LD_BIND_NOW=0",
+      NULL,
+    };
+    struct support_capture_subprocess result
+      = support_capture_subprogram (spargs[0], spargs, envs);
+    support_capture_subprocess_check (&result,
+				      "tst-tunables-enable_secure-env",
+				      0,
+				      sc_allow_none);
+    support_capture_subprocess_free (&result);
+  }
+
   return 0;
 }