314 lines
10 KiB
Diff
314 lines
10 KiB
Diff
commit f0e23d34a7bdf6b90fba954ee741419171ac41b2
|
|
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
|
|
Date: Mon Jul 19 18:42:26 2021 -0300
|
|
|
|
elf: Issue audit la_objopen for vDSO
|
|
|
|
The vDSO is is listed in the link_map chain, but is never the subject of
|
|
an la_objopen call. A new internal flag __RTLD_VDSO is added that
|
|
acts as __RTLD_OPENEXEC to allocate the required 'struct auditstate'
|
|
extra space for the 'struct link_map'.
|
|
|
|
The return value from the callback is currently ignored, since there
|
|
is no PLT call involved by glibc when using the vDSO, neither the vDSO
|
|
are exported directly.
|
|
|
|
Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
|
|
|
|
Reviewed-by: Florian Weimer <fweimer@redhat.com>
|
|
|
|
Conflicts:
|
|
elf/Makefile
|
|
|
|
diff --git a/elf/Makefile b/elf/Makefile
|
|
index d8d9734df0fea9a8..f047c1cce0c55da0 100644
|
|
--- a/elf/Makefile
|
|
+++ b/elf/Makefile
|
|
@@ -222,6 +222,7 @@ tests += restest1 preloadtest loadfail multiload origtest resolvfail \
|
|
tst-audit17 \
|
|
tst-audit18 \
|
|
tst-audit19b \
|
|
+ tst-audit22 \
|
|
# reldep9
|
|
tests-internal += loadtest unload unload2 circleload1 \
|
|
neededtest neededtest2 neededtest3 neededtest4 \
|
|
@@ -363,6 +364,7 @@ modules-names = testobj1 testobj2 testobj3 testobj4 testobj5 testobj6 \
|
|
tst-auditmod19a \
|
|
tst-auditmod19b \
|
|
tst-audit19bmod \
|
|
+ tst-auditmod22 \
|
|
|
|
# Most modules build with _ISOMAC defined, but those filtered out
|
|
# depend on internal headers.
|
|
@@ -1577,6 +1579,9 @@ $(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
|
|
$(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
|
|
tst-audit19b-ARGS = -- $(host-test-program-cmd)
|
|
|
|
+$(objpfx)tst-audit22.out: $(objpfx)tst-auditmod22.so
|
|
+tst-audit22-ARGS = -- $(host-test-program-cmd)
|
|
+
|
|
# tst-sonamemove links against an older implementation of the library.
|
|
LDFLAGS-tst-sonamemove-linkmod1.so = \
|
|
-Wl,--version-script=tst-sonamemove-linkmod1.map \
|
|
diff --git a/elf/dl-object.c b/elf/dl-object.c
|
|
index 05a7750c65305771..3be309ecf1b5d4e2 100644
|
|
--- a/elf/dl-object.c
|
|
+++ b/elf/dl-object.c
|
|
@@ -59,16 +59,19 @@ _dl_new_object (char *realname, const char *libname, int type,
|
|
{
|
|
#ifdef SHARED
|
|
unsigned int naudit;
|
|
- if (__glibc_unlikely ((mode & __RTLD_OPENEXEC) != 0))
|
|
+ if (__glibc_unlikely ((mode & (__RTLD_OPENEXEC | __RTLD_VDSO)) != 0))
|
|
{
|
|
- assert (type == lt_executable);
|
|
- assert (nsid == LM_ID_BASE);
|
|
+ if (mode & __RTLD_OPENEXEC)
|
|
+ {
|
|
+ assert (type == lt_executable);
|
|
+ assert (nsid == LM_ID_BASE);
|
|
|
|
- /* Ignore the specified libname for the main executable. It is
|
|
- only known with an explicit loader invocation. */
|
|
- libname = "";
|
|
+ /* Ignore the specified libname for the main executable. It is
|
|
+ only known with an explicit loader invocation. */
|
|
+ libname = "";
|
|
+ }
|
|
|
|
- /* We create the map for the executable before we know whether
|
|
+ /* We create the map for the executable and vDSO before we know whether
|
|
we have auditing libraries and if yes, how many. Assume the
|
|
worst. */
|
|
naudit = DL_NNS;
|
|
diff --git a/elf/rtld.c b/elf/rtld.c
|
|
index 2994578ba3a5f911..efcbeac6c24c4b7b 100644
|
|
--- a/elf/rtld.c
|
|
+++ b/elf/rtld.c
|
|
@@ -1917,6 +1917,12 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
|
|
assert (i == npreloads);
|
|
}
|
|
|
|
+#ifdef NEED_DL_SYSINFO_DSO
|
|
+ /* Now that the audit modules are opened, call la_objopen for the vDSO. */
|
|
+ if (GLRO(dl_sysinfo_map) != NULL)
|
|
+ _dl_audit_objopen (GLRO(dl_sysinfo_map), LM_ID_BASE);
|
|
+#endif
|
|
+
|
|
/* Load all the libraries specified by DT_NEEDED entries. If LD_PRELOAD
|
|
specified some libraries to load, these are inserted before the actual
|
|
dependencies in the executable's searchlist for symbol resolution. */
|
|
diff --git a/elf/setup-vdso.h b/elf/setup-vdso.h
|
|
index 34b1d5e8c37c2610..d2b35a080b57c183 100644
|
|
--- a/elf/setup-vdso.h
|
|
+++ b/elf/setup-vdso.h
|
|
@@ -30,7 +30,7 @@ setup_vdso (struct link_map *main_map __attribute__ ((unused)),
|
|
We just want our data structures to describe it as if we had just
|
|
mapped and relocated it normally. */
|
|
struct link_map *l = _dl_new_object ((char *) "", "", lt_library, NULL,
|
|
- 0, LM_ID_BASE);
|
|
+ __RTLD_VDSO, LM_ID_BASE);
|
|
if (__glibc_likely (l != NULL))
|
|
{
|
|
static ElfW(Dyn) dyn_temp[DL_RO_DYN_TEMP_CNT] attribute_relro;
|
|
diff --git a/elf/tst-audit22.c b/elf/tst-audit22.c
|
|
new file mode 100644
|
|
index 0000000000000000..18fd22a760ddc3d8
|
|
--- /dev/null
|
|
+++ b/elf/tst-audit22.c
|
|
@@ -0,0 +1,124 @@
|
|
+/* Check DTAUDIT and vDSO interaction.
|
|
+ Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ This file is part of the GNU C Library.
|
|
+
|
|
+ The GNU C Library is free software; you can redistribute it and/or
|
|
+ modify it under the terms of the GNU Lesser General Public
|
|
+ License as published by the Free Software Foundation; either
|
|
+ version 2.1 of the License, or (at your option) any later version.
|
|
+
|
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public
|
|
+ License along with the GNU C Library; if not, see
|
|
+ <https://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <errno.h>
|
|
+#include <getopt.h>
|
|
+#include <limits.h>
|
|
+#include <inttypes.h>
|
|
+#include <string.h>
|
|
+#include <stdlib.h>
|
|
+#include <support/capture_subprocess.h>
|
|
+#include <support/check.h>
|
|
+#include <support/xstdio.h>
|
|
+#include <support/support.h>
|
|
+#include <sys/auxv.h>
|
|
+
|
|
+static int restart;
|
|
+#define CMDLINE_OPTIONS \
|
|
+ { "restart", no_argument, &restart, 1 },
|
|
+
|
|
+static uintptr_t vdso_addr;
|
|
+
|
|
+static int
|
|
+handle_restart (void)
|
|
+{
|
|
+ fprintf (stderr, "vdso: %p\n", (void*) vdso_addr);
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+static uintptr_t
|
|
+parse_address (const char *str)
|
|
+{
|
|
+ void *r;
|
|
+ TEST_COMPARE (sscanf (str, "%p\n", &r), 1);
|
|
+ return (uintptr_t) r;
|
|
+}
|
|
+
|
|
+static inline bool
|
|
+startswith (const char *str, const char *pre)
|
|
+{
|
|
+ size_t lenpre = strlen (pre);
|
|
+ size_t lenstr = strlen (str);
|
|
+ return lenstr >= lenpre && memcmp (pre, str, lenpre) == 0;
|
|
+}
|
|
+
|
|
+static int
|
|
+do_test (int argc, char *argv[])
|
|
+{
|
|
+ vdso_addr = getauxval (AT_SYSINFO_EHDR);
|
|
+ if (vdso_addr == 0)
|
|
+ FAIL_UNSUPPORTED ("getauxval (AT_SYSINFO_EHDR) returned 0");
|
|
+
|
|
+ /* We must have either:
|
|
+ - One our fource parameters left if called initially:
|
|
+ + path to ld.so optional
|
|
+ + "--library-path" optional
|
|
+ + the library path optional
|
|
+ + the application name */
|
|
+ if (restart)
|
|
+ return handle_restart ();
|
|
+
|
|
+ char *spargv[9];
|
|
+ int i = 0;
|
|
+ for (; i < argc - 1; i++)
|
|
+ spargv[i] = argv[i + 1];
|
|
+ spargv[i++] = (char *) "--direct";
|
|
+ spargv[i++] = (char *) "--restart";
|
|
+ spargv[i] = NULL;
|
|
+
|
|
+ setenv ("LD_AUDIT", "tst-auditmod22.so", 0);
|
|
+ struct support_capture_subprocess result
|
|
+ = support_capture_subprogram (spargv[0], spargv);
|
|
+ support_capture_subprocess_check (&result, "tst-audit22", 0, sc_allow_stderr);
|
|
+
|
|
+ /* The respawned process should always print the vDSO address (otherwise it
|
|
+ will fails as unsupported). However, on some architectures the audit
|
|
+ module might see the vDSO with l_addr being 0, meaning a fixed mapping
|
|
+ (linux-gate.so). In this case we don't check its value against
|
|
+ AT_SYSINFO_EHDR one. */
|
|
+ uintptr_t vdso_process = 0;
|
|
+ bool vdso_audit_found = false;
|
|
+ uintptr_t vdso_audit = 0;
|
|
+
|
|
+ FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
|
|
+ TEST_VERIFY (out != NULL);
|
|
+ char *buffer = NULL;
|
|
+ size_t buffer_length = 0;
|
|
+ while (xgetline (&buffer, &buffer_length, out))
|
|
+ {
|
|
+ if (startswith (buffer, "vdso: "))
|
|
+ vdso_process = parse_address (buffer + strlen ("vdso: "));
|
|
+ else if (startswith (buffer, "vdso found: "))
|
|
+ {
|
|
+ vdso_audit = parse_address (buffer + strlen ("vdso found: "));
|
|
+ vdso_audit_found = true;
|
|
+ }
|
|
+ }
|
|
+
|
|
+ TEST_COMPARE (vdso_audit_found, true);
|
|
+ if (vdso_audit != 0)
|
|
+ TEST_COMPARE (vdso_process, vdso_audit);
|
|
+
|
|
+ free (buffer);
|
|
+ xfclose (out);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
+
|
|
+#define TEST_FUNCTION_ARGV do_test
|
|
+#include <support/test-driver.c>
|
|
diff --git a/elf/tst-auditmod22.c b/elf/tst-auditmod22.c
|
|
new file mode 100644
|
|
index 0000000000000000..8e05ce8cbb215dd5
|
|
--- /dev/null
|
|
+++ b/elf/tst-auditmod22.c
|
|
@@ -0,0 +1,51 @@
|
|
+/* Check DTAUDIT and vDSO interaction.
|
|
+ Copyright (C) 2021 Free Software Foundation, Inc.
|
|
+ This file is part of the GNU C Library.
|
|
+
|
|
+ The GNU C Library is free software; you can redistribute it and/or
|
|
+ modify it under the terms of the GNU Lesser General Public
|
|
+ License as published by the Free Software Foundation; either
|
|
+ version 2.1 of the License, or (at your option) any later version.
|
|
+
|
|
+ The GNU C Library is distributed in the hope that it will be useful,
|
|
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
+ Lesser General Public License for more details.
|
|
+
|
|
+ You should have received a copy of the GNU Lesser General Public
|
|
+ License along with the GNU C Library; if not, see
|
|
+ <https://www.gnu.org/licenses/>. */
|
|
+
|
|
+#include <link.h>
|
|
+#include <inttypes.h>
|
|
+#include <stdbool.h>
|
|
+#include <string.h>
|
|
+#include <stdio.h>
|
|
+#include <sys/auxv.h>
|
|
+
|
|
+static inline bool
|
|
+startswith (const char *str, const char *pre)
|
|
+{
|
|
+ size_t lenpre = strlen (pre);
|
|
+ size_t lenstr = strlen (str);
|
|
+ return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
|
|
+}
|
|
+
|
|
+unsigned int
|
|
+la_version (unsigned int version)
|
|
+{
|
|
+ return LAV_CURRENT;
|
|
+}
|
|
+
|
|
+unsigned int
|
|
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
|
|
+{
|
|
+ /* The linux-gate.so is placed at a fixed address, thus l_addr being 0,
|
|
+ and it might be the value reported as the AT_SYSINFO_EHDR. */
|
|
+ if (map->l_addr == 0 && startswith (map->l_name, "linux-gate.so"))
|
|
+ fprintf (stderr, "vdso found: %p\n", NULL);
|
|
+ else if (map->l_addr == getauxval (AT_SYSINFO_EHDR))
|
|
+ fprintf (stderr, "vdso found: %p\n", (void*) map->l_addr);
|
|
+
|
|
+ return 0;
|
|
+}
|
|
diff --git a/include/dlfcn.h b/include/dlfcn.h
|
|
index 109586a1d968b630..a39cc9c69f55a56a 100644
|
|
--- a/include/dlfcn.h
|
|
+++ b/include/dlfcn.h
|
|
@@ -12,6 +12,8 @@
|
|
#define __RTLD_AUDIT 0x08000000
|
|
#define __RTLD_SECURE 0x04000000 /* Apply additional security checks. */
|
|
#define __RTLD_NOIFUNC 0x02000000 /* Suppress calling ifunc functions. */
|
|
+#define __RTLD_VDSO 0x01000000 /* Tell _dl_new_object the object is
|
|
+ system-loaded. */
|
|
|
|
#define __LM_ID_CALLER -2
|
|
|