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