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