|
|
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 |
{
|