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