08c3a6
commit c73c79af7d6f1124fbfa5d935b4f620217d6a2ec
08c3a6
Author: Szabolcs Nagy <szabolcs.nagy@arm.com>
08c3a6
Date:   Fri Jun 15 16:14:58 2018 +0100
08c3a6
08c3a6
    rtld: Use generic argv adjustment in ld.so [BZ #23293]
08c3a6
    
08c3a6
    When an executable is invoked as
08c3a6
    
08c3a6
      ./ld.so [ld.so-args] ./exe [exe-args]
08c3a6
    
08c3a6
    then the argv is adujusted in ld.so before calling the entry point of
08c3a6
    the executable so ld.so args are not visible to it.  On most targets
08c3a6
    this requires moving argv, env and auxv on the stack to ensure correct
08c3a6
    stack alignment at the entry point.  This had several issues:
08c3a6
    
08c3a6
    - The code for this adjustment on the stack is written in asm as part
08c3a6
      of the target specific ld.so _start code which is hard to maintain.
08c3a6
    
08c3a6
    - The adjustment is done after _dl_start returns, where it's too late
08c3a6
      to update GLRO(dl_auxv), as it is already readonly, so it points to
08c3a6
      memory that was clobbered by the adjustment. This is bug 23293.
08c3a6
    
08c3a6
    - _environ is also wrong in ld.so after the adjustment, but it is
08c3a6
      likely not used after _dl_start returns so this is not user visible.
08c3a6
    
08c3a6
    - _dl_argv was updated, but for this it was moved out of relro, which
08c3a6
      changes security properties across targets unnecessarily.
08c3a6
    
08c3a6
    This patch introduces a generic _dl_start_args_adjust function that
08c3a6
    handles the argument adjustments after ld.so processed its own args
08c3a6
    and before relro protection is applied.
08c3a6
    
08c3a6
    The same algorithm is used on all targets, _dl_skip_args is now 0, so
08c3a6
    existing target specific adjustment code is no longer used.  The bug
08c3a6
    affects aarch64, alpha, arc, arm, csky, ia64, nios2, s390-32 and sparc,
08c3a6
    other targets don't need the change in principle, only for consistency.
08c3a6
    
08c3a6
    The GNU Hurd start code relied on _dl_skip_args after dl_main returned,
08c3a6
    now it checks directly if args were adjusted and fixes the Hurd startup
08c3a6
    data accordingly.
08c3a6
    
08c3a6
    Follow up patches can remove _dl_skip_args and DL_ARGV_NOT_RELRO.
08c3a6
    
08c3a6
    Tested on aarch64-linux-gnu and cross tested on i686-gnu.
08c3a6
    
08c3a6
    Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
08c3a6
    (cherry picked from commit ad43cac44a6860eaefcadadfb2acb349921e96bf)
08c3a6
08c3a6
Conflicts:
08c3a6
	elf/rtld.c
08c3a6
          (Downstream-only backport of glibc-rh2023422-1.patch)
08c3a6
08c3a6
diff --git a/elf/rtld.c b/elf/rtld.c
08c3a6
index 434fbeddd5cce74d..9de53ccaed420a57 100644
08c3a6
--- a/elf/rtld.c
08c3a6
+++ b/elf/rtld.c
08c3a6
@@ -1121,6 +1121,62 @@ rtld_chain_load (struct link_map *main_map, char *argv0)
08c3a6
 		     rtld_soname, pathname, errcode);
08c3a6
 }
08c3a6
 
08c3a6
+/* Adjusts the contents of the stack and related globals for the user
08c3a6
+   entry point.  The ld.so processed skip_args arguments and bumped
08c3a6
+   _dl_argv and _dl_argc accordingly.  Those arguments are removed from
08c3a6
+   argv here.  */
08c3a6
+static void
08c3a6
+_dl_start_args_adjust (int skip_args)
08c3a6
+{
08c3a6
+  void **sp = (void **) (_dl_argv - skip_args - 1);
08c3a6
+  void **p = sp + skip_args;
08c3a6
+
08c3a6
+  if (skip_args == 0)
08c3a6
+    return;
08c3a6
+
08c3a6
+  /* Sanity check.  */
08c3a6
+  intptr_t argc = (intptr_t) sp[0] - skip_args;
08c3a6
+  assert (argc == _dl_argc);
08c3a6
+
08c3a6
+  /* Adjust argc on stack.  */
08c3a6
+  sp[0] = (void *) (intptr_t) _dl_argc;
08c3a6
+
08c3a6
+  /* Update globals in rtld.  */
08c3a6
+  _dl_argv -= skip_args;
08c3a6
+  _environ -= skip_args;
08c3a6
+
08c3a6
+  /* Shuffle argv down.  */
08c3a6
+  do
08c3a6
+    *++sp = *++p;
08c3a6
+  while (*p != NULL);
08c3a6
+
08c3a6
+  assert (_environ == (char **) (sp + 1));
08c3a6
+
08c3a6
+  /* Shuffle envp down.  */
08c3a6
+  do
08c3a6
+    *++sp = *++p;
08c3a6
+  while (*p != NULL);
08c3a6
+
08c3a6
+#ifdef HAVE_AUX_VECTOR
08c3a6
+  void **auxv = (void **) GLRO(dl_auxv) - skip_args;
08c3a6
+  GLRO(dl_auxv) = (ElfW(auxv_t) *) auxv; /* Aliasing violation.  */
08c3a6
+  assert (auxv == sp + 1);
08c3a6
+
08c3a6
+  /* Shuffle auxv down. */
08c3a6
+  ElfW(auxv_t) ax;
08c3a6
+  char *oldp = (char *) (p + 1);
08c3a6
+  char *newp = (char *) (sp + 1);
08c3a6
+  do
08c3a6
+    {
08c3a6
+      memcpy (&ax, oldp, sizeof (ax));
08c3a6
+      memcpy (newp, &ax, sizeof (ax));
08c3a6
+      oldp += sizeof (ax);
08c3a6
+      newp += sizeof (ax);
08c3a6
+    }
08c3a6
+  while (ax.a_type != AT_NULL);
08c3a6
+#endif
08c3a6
+}
08c3a6
+
08c3a6
 static void
08c3a6
 dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	 ElfW(Word) phnum,
08c3a6
@@ -1177,6 +1233,7 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
       rtld_is_main = true;
08c3a6
 
08c3a6
       char *argv0 = NULL;
08c3a6
+      char **orig_argv = _dl_argv;
08c3a6
 
08c3a6
       /* Note the place where the dynamic linker actually came from.  */
08c3a6
       GL(dl_rtld_map).l_name = rtld_progname;
08c3a6
@@ -1191,7 +1248,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 		GLRO(dl_lazy) = -1;
08c3a6
 	      }
08c3a6
 
08c3a6
-	    ++_dl_skip_args;
08c3a6
 	    --_dl_argc;
08c3a6
 	    ++_dl_argv;
08c3a6
 	  }
08c3a6
@@ -1200,14 +1256,12 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	    if (state.mode != rtld_mode_help)
08c3a6
 	      state.mode = rtld_mode_verify;
08c3a6
 
08c3a6
-	    ++_dl_skip_args;
08c3a6
 	    --_dl_argc;
08c3a6
 	    ++_dl_argv;
08c3a6
 	  }
08c3a6
 	else if (! strcmp (_dl_argv[1], "--inhibit-cache"))
08c3a6
 	  {
08c3a6
 	    GLRO(dl_inhibit_cache) = 1;
08c3a6
-	    ++_dl_skip_args;
08c3a6
 	    --_dl_argc;
08c3a6
 	    ++_dl_argv;
08c3a6
 	  }
08c3a6
@@ -1217,7 +1271,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	    state.library_path = _dl_argv[2];
08c3a6
 	    state.library_path_source = "--library-path";
08c3a6
 
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
@@ -1226,7 +1279,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	  {
08c3a6
 	    GLRO(dl_inhibit_rpath) = _dl_argv[2];
08c3a6
 
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
@@ -1234,14 +1286,12 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	  {
08c3a6
 	    audit_list_add_string (&state.audit_list, _dl_argv[2]);
08c3a6
 
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
 	else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
08c3a6
 	  {
08c3a6
 	    state.preloadarg = _dl_argv[2];
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
@@ -1249,7 +1299,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	  {
08c3a6
 	    argv0 = _dl_argv[2];
08c3a6
 
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
@@ -1257,7 +1306,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 		 && _dl_argc > 2)
08c3a6
 	  {
08c3a6
 	    state.glibc_hwcaps_prepend = _dl_argv[2];
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
@@ -1265,7 +1313,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 		 && _dl_argc > 2)
08c3a6
 	  {
08c3a6
 	    state.glibc_hwcaps_mask = _dl_argv[2];
08c3a6
-	    _dl_skip_args += 2;
08c3a6
 	    _dl_argc -= 2;
08c3a6
 	    _dl_argv += 2;
08c3a6
 	  }
08c3a6
@@ -1274,7 +1321,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	  {
08c3a6
 	    state.mode = rtld_mode_list_tunables;
08c3a6
 
08c3a6
-	    ++_dl_skip_args;
08c3a6
 	    --_dl_argc;
08c3a6
 	    ++_dl_argv;
08c3a6
 	  }
08c3a6
@@ -1283,7 +1329,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	  {
08c3a6
 	    state.mode = rtld_mode_list_diagnostics;
08c3a6
 
08c3a6
-	    ++_dl_skip_args;
08c3a6
 	    --_dl_argc;
08c3a6
 	    ++_dl_argv;
08c3a6
 	  }
08c3a6
@@ -1329,7 +1374,6 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
 	    _dl_usage (ld_so_name, NULL);
08c3a6
 	}
08c3a6
 
08c3a6
-      ++_dl_skip_args;
08c3a6
       --_dl_argc;
08c3a6
       ++_dl_argv;
08c3a6
 
08c3a6
@@ -1428,6 +1472,9 @@ dl_main (const ElfW(Phdr) *phdr,
08c3a6
       /* Set the argv[0] string now that we've processed the executable.  */
08c3a6
       if (argv0 != NULL)
08c3a6
         _dl_argv[0] = argv0;
08c3a6
+
08c3a6
+      /* Adjust arguments for the application entry point.  */
08c3a6
+      _dl_start_args_adjust (_dl_argv - orig_argv);
08c3a6
     }
08c3a6
   else
08c3a6
     {
08c3a6
diff --git a/sysdeps/mach/hurd/dl-sysdep.c b/sysdeps/mach/hurd/dl-sysdep.c
08c3a6
index 4b2072e5d5e3bfd2..5c0f8e46bfbd4753 100644
08c3a6
--- a/sysdeps/mach/hurd/dl-sysdep.c
08c3a6
+++ b/sysdeps/mach/hurd/dl-sysdep.c
08c3a6
@@ -106,6 +106,7 @@ _dl_sysdep_start (void **start_argptr,
08c3a6
 {
08c3a6
   void go (intptr_t *argdata)
08c3a6
     {
08c3a6
+      char *orig_argv0;
08c3a6
       char **p;
08c3a6
 
08c3a6
       /* Cache the information in various global variables.  */
08c3a6
@@ -114,6 +115,8 @@ _dl_sysdep_start (void **start_argptr,
08c3a6
       _environ = &_dl_argv[_dl_argc + 1];
08c3a6
       for (p = _environ; *p++;); /* Skip environ pointers and terminator.  */
08c3a6
 
08c3a6
+      orig_argv0 = _dl_argv[0];
08c3a6
+
08c3a6
       if ((void *) p == _dl_argv[0])
08c3a6
 	{
08c3a6
 	  static struct hurd_startup_data nodata;
08c3a6
@@ -204,30 +207,23 @@ unfmh();			/* XXX */
08c3a6
 
08c3a6
       /* The call above might screw a few things up.
08c3a6
 
08c3a6
-	 First of all, if _dl_skip_args is nonzero, we are ignoring
08c3a6
-	 the first few arguments.  However, if we have no Hurd startup
08c3a6
-	 data, it is the magical convention that ARGV[0] == P.  The
08c3a6
+	 P is the location after the terminating NULL of the list of
08c3a6
+	 environment variables.  It has to point to the Hurd startup
08c3a6
+	 data or if that's missing then P == ARGV[0] must hold. The
08c3a6
 	 startup code in init-first.c will get confused if this is not
08c3a6
 	 the case, so we must rearrange things to make it so.  We'll
08c3a6
-	 overwrite the origional ARGV[0] at P with ARGV[_dl_skip_args].
08c3a6
+	 recompute P and move the Hurd data or the new ARGV[0] there.
08c3a6
 
08c3a6
-	 Secondly, if we need to be secure, it removes some dangerous
08c3a6
-	 environment variables.  If we have no Hurd startup date this
08c3a6
-	 changes P (since that's the location after the terminating
08c3a6
-	 NULL in the list of environment variables).  We do the same
08c3a6
-	 thing as in the first case but make sure we recalculate P.
08c3a6
-	 If we do have Hurd startup data, we have to move the data
08c3a6
-	 such that it starts just after the terminating NULL in the
08c3a6
-	 environment list.
08c3a6
+	 Note: directly invoked ld.so can move arguments and env vars.
08c3a6
 
08c3a6
 	 We use memmove, since the locations might overlap.  */
08c3a6
-      if (__libc_enable_secure || _dl_skip_args)
08c3a6
-	{
08c3a6
-	  char **newp;
08c3a6
 
08c3a6
-	  for (newp = _environ; *newp++;);
08c3a6
+      char **newp;
08c3a6
+      for (newp = _environ; *newp++;);
08c3a6
 
08c3a6
-	  if (_dl_argv[-_dl_skip_args] == (char *) p)
08c3a6
+      if (newp != p || _dl_argv[0] != orig_argv0)
08c3a6
+	{
08c3a6
+	  if (orig_argv0 == (char *) p)
08c3a6
 	    {
08c3a6
 	      if ((char *) newp != _dl_argv[0])
08c3a6
 		{