076f82
commit 29496b3103ff13aa3c1d8b62552a98f39da0fe59
076f82
Author: Adhemerval Zanella <adhemerval.zanella@linaro.org>
076f82
Date:   Wed Jun 30 10:24:09 2021 -0300
076f82
076f82
    elf: Avoid unnecessary slowdown from profiling with audit (BZ#15533)
076f82
    
076f82
    The rtld-audit interfaces introduces a slowdown due to enabling
076f82
    profiling instrumentation (as if LD_AUDIT implied LD_PROFILE).
076f82
    However, instrumenting is only necessary if one of audit libraries
076f82
    provides PLT callbacks (la_pltenter or la_pltexit symbols).  Otherwise,
076f82
    the slowdown can be avoided.
076f82
    
076f82
    The following patch adjusts the logic that enables profiling to iterate
076f82
    over all audit modules and check if any of those provides a PLT hook.
076f82
    To keep la_symbind to work even without PLT callbacks, _dl_fixup now
076f82
    calls the audit callback if the modules implements it.
076f82
    
076f82
    Co-authored-by: Alexander Monakov <amonakov@ispras.ru>
076f82
    
076f82
    Checked on x86_64-linux-gnu, i686-linux-gnu, and aarch64-linux-gnu.
076f82
    
076f82
    Reviewed-by: Florian Weimer <fweimer@redhat.com>
076f82
    (cherry picked from commit 063f9ba220f434c7f30dd65c4cff17c0c458a7cf)
076f82
    
076f82
    Resolved conflicts:
076f82
            NEWS
076f82
            elf/Makefile
076f82
076f82
diff --git a/elf/Makefile b/elf/Makefile
076f82
index 85165c0591412a45..eab9d46b6165e6be 100644
076f82
--- a/elf/Makefile
076f82
+++ b/elf/Makefile
076f82
@@ -365,6 +365,7 @@ tests += \
076f82
   tst-audit16 \
076f82
   tst-audit17 \
076f82
   tst-audit18 \
076f82
+  tst-audit19b \
076f82
   tst-auditmany \
076f82
   tst-auxobj \
076f82
   tst-auxobj-dlopen \
076f82
@@ -454,6 +455,7 @@ tests-internal += \
076f82
   neededtest2 \
076f82
   neededtest3 \
076f82
   neededtest4 \
076f82
+  tst-audit19a \
076f82
   tst-create_format1 \
076f82
   tst-dl-hwcaps_split \
076f82
   tst-dlmopen2 \
076f82
@@ -626,6 +628,7 @@ modules-names = \
076f82
   tst-audit12mod3 \
076f82
   tst-audit13mod1 \
076f82
   tst-audit18mod \
076f82
+  tst-audit19bmod \
076f82
   tst-auditlogmod-1 \
076f82
   tst-auditlogmod-2 \
076f82
   tst-auditlogmod-3 \
076f82
@@ -644,6 +647,8 @@ modules-names = \
076f82
   tst-auditmod11 \
076f82
   tst-auditmod12 \
076f82
   tst-auditmod18 \
076f82
+  tst-auditmod19a \
076f82
+  tst-auditmod19b \
076f82
   tst-auxvalmod \
076f82
   tst-big-note-lib \
076f82
   tst-deep1mod1 \
076f82
@@ -2007,6 +2012,13 @@ $(objpfx)tst-audit18.out: $(objpfx)tst-auditmod18.so \
076f82
 			  $(objpfx)tst-audit18mod.so
076f82
 tst-audit18-ARGS = -- $(host-test-program-cmd)
076f82
 
076f82
+$(objpfx)tst-audit19a.out: $(objpfx)tst-auditmod19a.so
076f82
+tst-audit19a-ENV = LD_AUDIT=$(objpfx)tst-auditmod19a.so
076f82
+
076f82
+$(objpfx)tst-audit19b.out: $(objpfx)tst-auditmod19b.so
076f82
+$(objpfx)tst-audit19b: $(objpfx)tst-audit19bmod.so
076f82
+tst-audit19b-ARGS = -- $(host-test-program-cmd)
076f82
+
076f82
 # tst-sonamemove links against an older implementation of the library.
076f82
 LDFLAGS-tst-sonamemove-linkmod1.so = \
076f82
   -Wl,--version-script=tst-sonamemove-linkmod1.map \
076f82
diff --git a/elf/dl-reloc.c b/elf/dl-reloc.c
076f82
index 3447de7f3536cd70..5b69321bda1f2b27 100644
076f82
--- a/elf/dl-reloc.c
076f82
+++ b/elf/dl-reloc.c
076f82
@@ -205,12 +205,28 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
076f82
   int skip_ifunc = reloc_mode & __RTLD_NOIFUNC;
076f82
 
076f82
 #ifdef SHARED
076f82
+  bool consider_symbind = false;
076f82
   /* If we are auditing, install the same handlers we need for profiling.  */
076f82
   if ((reloc_mode & __RTLD_AUDIT) == 0)
076f82
-    consider_profiling |= GLRO(dl_audit) != NULL;
076f82
+    {
076f82
+      struct audit_ifaces *afct = GLRO(dl_audit);
076f82
+      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
076f82
+	{
076f82
+	  /* Profiling is needed only if PLT hooks are provided.  */
076f82
+	  if (afct->ARCH_LA_PLTENTER != NULL
076f82
+	      || afct->ARCH_LA_PLTEXIT != NULL)
076f82
+	    consider_profiling = 1;
076f82
+	  if (afct->symbind != NULL)
076f82
+	    consider_symbind = true;
076f82
+
076f82
+	  afct = afct->next;
076f82
+	}
076f82
+    }
076f82
 #elif defined PROF
076f82
   /* Never use dynamic linker profiling for gprof profiling code.  */
076f82
 # define consider_profiling 0
076f82
+#else
076f82
+# define consider_symbind 0
076f82
 #endif
076f82
 
076f82
   if (l->l_relocated)
076f82
@@ -272,7 +288,7 @@ _dl_relocate_object (struct link_map *l, struct r_scope_elem *scope[],
076f82
     ELF_DYNAMIC_RELOCATE (l, scope, lazy, consider_profiling, skip_ifunc);
076f82
 
076f82
 #ifndef PROF
076f82
-    if (__glibc_unlikely (consider_profiling)
076f82
+    if ((consider_profiling || consider_symbind)
076f82
 	&& l->l_info[DT_PLTRELSZ] != NULL)
076f82
       {
076f82
 	/* Allocate the array which will contain the already found
076f82
diff --git a/elf/dl-runtime.c b/elf/dl-runtime.c
076f82
index e42f6e8b8dfca08e..77a5cccdcbcb9293 100644
076f82
--- a/elf/dl-runtime.c
076f82
+++ b/elf/dl-runtime.c
076f82
@@ -124,6 +124,37 @@ _dl_fixup (
076f82
       && __builtin_expect (ELFW(ST_TYPE) (sym->st_info) == STT_GNU_IFUNC, 0))
076f82
     value = elf_ifunc_invoke (DL_FIXUP_VALUE_ADDR (value));
076f82
 
076f82
+#ifdef SHARED
076f82
+  /* Auditing checkpoint: we have a new binding.  Provide the auditing
076f82
+     libraries the possibility to change the value and tell us whether further
076f82
+     auditing is wanted.
076f82
+     The l_reloc_result is only allocated if there is an audit module which
076f82
+     provides a la_symbind.  */
076f82
+  if (l->l_reloc_result != NULL)
076f82
+    {
076f82
+      /* This is the address in the array where we store the result of previous
076f82
+	 relocations.  */
076f82
+      struct reloc_result *reloc_result
076f82
+	= &l->l_reloc_result[reloc_index (pltgot, reloc_arg, sizeof (PLTREL))];
076f82
+      unsigned int init = atomic_load_acquire (&reloc_result->init);
076f82
+      if (init == 0)
076f82
+	{
076f82
+	  _dl_audit_symbind (l, reloc_result, sym, &value, result);
076f82
+
076f82
+	  /* Store the result for later runs.  */
076f82
+	  if (__glibc_likely (! GLRO(dl_bind_not)))
076f82
+	    {
076f82
+	      reloc_result->addr = value;
076f82
+	      /* Guarantee all previous writes complete before init is
076f82
+		 updated.  See CONCURRENCY NOTES below.  */
076f82
+	      atomic_store_release (&reloc_result->init, 1);
076f82
+	    }
076f82
+	}
076f82
+      else
076f82
+	value = reloc_result->addr;
076f82
+    }
076f82
+#endif
076f82
+
076f82
   /* Finally, fix up the plt itself.  */
076f82
   if (__glibc_unlikely (GLRO(dl_bind_not)))
076f82
     return value;
076f82
diff --git a/elf/rtld.c b/elf/rtld.c
076f82
index b6bb46ca97b7972f..f632a767d7a269ef 100644
076f82
--- a/elf/rtld.c
076f82
+++ b/elf/rtld.c
076f82
@@ -1016,13 +1016,7 @@ ERROR: audit interface '%s' requires version %d (maximum supported version %d);
076f82
     "la_objsearch\0"
076f82
     "la_objopen\0"
076f82
     "la_preinit\0"
076f82
-#if __ELF_NATIVE_CLASS == 32
076f82
-    "la_symbind32\0"
076f82
-#elif __ELF_NATIVE_CLASS == 64
076f82
-    "la_symbind64\0"
076f82
-#else
076f82
-# error "__ELF_NATIVE_CLASS must be defined"
076f82
-#endif
076f82
+    LA_SYMBIND "\0"
076f82
 #define STRING(s) __STRING (s)
076f82
     "la_" STRING (ARCH_LA_PLTENTER) "\0"
076f82
     "la_" STRING (ARCH_LA_PLTEXIT) "\0"
076f82
diff --git a/elf/tst-audit19a.c b/elf/tst-audit19a.c
076f82
new file mode 100644
076f82
index 0000000000000000..035cde9351c2711b
076f82
--- /dev/null
076f82
+++ b/elf/tst-audit19a.c
076f82
@@ -0,0 +1,38 @@
076f82
+/* Check if DT_AUDIT a module without la_plt{enter,exit} symbols does not incur
076f82
+   in profiling (BZ#15533).
076f82
+   Copyright (C) 2021 Free Software Foundation, Inc.
076f82
+   This file is part of the GNU C Library.
076f82
+
076f82
+   The GNU C Library is free software; you can redistribute it and/or
076f82
+   modify it under the terms of the GNU Lesser General Public
076f82
+   License as published by the Free Software Foundation; either
076f82
+   version 2.1 of the License, or (at your option) any later version.
076f82
+
076f82
+   The GNU C Library is distributed in the hope that it will be useful,
076f82
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
076f82
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
076f82
+   Lesser General Public License for more details.
076f82
+
076f82
+   You should have received a copy of the GNU Lesser General Public
076f82
+   License along with the GNU C Library; if not, see
076f82
+   <https://www.gnu.org/licenses/>.  */
076f82
+
076f82
+#include <link.h>
076f82
+#include <support/xdlfcn.h>
076f82
+#include <support/check.h>
076f82
+
076f82
+static int
076f82
+do_test (void)
076f82
+{
076f82
+  void *h = xdlopen ("tst-auditmod19a.so", RTLD_NOW);
076f82
+
076f82
+  struct link_map *lmap;
076f82
+  TEST_VERIFY_EXIT (dlinfo (h, RTLD_DI_LINKMAP, &lmap) == 0);
076f82
+
076f82
+  /* The internal array is only allocated if profiling is enabled.  */
076f82
+  TEST_VERIFY (lmap->l_reloc_result == NULL);
076f82
+
076f82
+  return 0;
076f82
+}
076f82
+
076f82
+#include <support/test-driver.c>
076f82
diff --git a/elf/tst-audit19b.c b/elf/tst-audit19b.c
076f82
new file mode 100644
076f82
index 0000000000000000..da015734f24e0d79
076f82
--- /dev/null
076f82
+++ b/elf/tst-audit19b.c
076f82
@@ -0,0 +1,94 @@
076f82
+/* Check if DT_AUDIT a module with la_plt{enter,exit} call la_symbind
076f82
+   for lazy resolution.
076f82
+   Copyright (C) 2021 Free Software Foundation, Inc.
076f82
+   This file is part of the GNU C Library.
076f82
+
076f82
+   The GNU C Library is free software; you can redistribute it and/or
076f82
+   modify it under the terms of the GNU Lesser General Public
076f82
+   License as published by the Free Software Foundation; either
076f82
+   version 2.1 of the License, or (at your option) any later version.
076f82
+
076f82
+   The GNU C Library is distributed in the hope that it will be useful,
076f82
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
076f82
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
076f82
+   Lesser General Public License for more details.
076f82
+
076f82
+   You should have received a copy of the GNU Lesser General Public
076f82
+   License along with the GNU C Library; if not, see
076f82
+   <https://www.gnu.org/licenses/>.  */
076f82
+
076f82
+#include <getopt.h>
076f82
+#include <support/capture_subprocess.h>
076f82
+#include <support/check.h>
076f82
+#include <support/xstdio.h>
076f82
+#include <stdlib.h>
076f82
+#include <string.h>
076f82
+#include <stdbool.h>
076f82
+
076f82
+static int restart;
076f82
+#define CMDLINE_OPTIONS \
076f82
+  { "restart", no_argument, &restart, 1 },
076f82
+
076f82
+int tst_audit18bmod1_func (void);
076f82
+
076f82
+static int
076f82
+handle_restart (void)
076f82
+{
076f82
+  TEST_COMPARE (tst_audit18bmod1_func (), 10);
076f82
+  return 0;
076f82
+}
076f82
+
076f82
+static inline bool
076f82
+startswith (const char *str, const char *pre)
076f82
+{
076f82
+  size_t lenpre = strlen (pre);
076f82
+  size_t lenstr = strlen (str);
076f82
+  return lenstr < lenpre ? false : memcmp (pre, str, lenpre) == 0;
076f82
+}
076f82
+
076f82
+static int
076f82
+do_test (int argc, char *argv[])
076f82
+{
076f82
+  /* We must have either:
076f82
+     - One our fource parameters left if called initially:
076f82
+       + path to ld.so         optional
076f82
+       + "--library-path"      optional
076f82
+       + the library path      optional
076f82
+       + the application name  */
076f82
+
076f82
+  if (restart)
076f82
+    return handle_restart ();
076f82
+
076f82
+  char *spargv[9];
076f82
+  int i = 0;
076f82
+  for (; i < argc - 1; i++)
076f82
+    spargv[i] = argv[i + 1];
076f82
+  spargv[i++] = (char *) "--direct";
076f82
+  spargv[i++] = (char *) "--restart";
076f82
+  spargv[i] = NULL;
076f82
+
076f82
+  setenv ("LD_AUDIT", "tst-auditmod18b.so", 0);
076f82
+  struct support_capture_subprocess result
076f82
+    = support_capture_subprogram (spargv[0], spargv);
076f82
+  support_capture_subprocess_check (&result, "tst-audit18b", 0, sc_allow_stderr);
076f82
+
076f82
+  bool find_symbind = false;
076f82
+
076f82
+  FILE *out = fmemopen (result.err.buffer, result.err.length, "r");
076f82
+  TEST_VERIFY (out != NULL);
076f82
+  char *buffer = NULL;
076f82
+  size_t buffer_length = 0;
076f82
+  while (xgetline (&buffer, &buffer_length, out))
076f82
+    if (startswith (buffer, "la_symbind: tst_audit18bmod1_func") == 0)
076f82
+      find_symbind = true;
076f82
+
076f82
+  TEST_COMPARE (find_symbind, true);
076f82
+
076f82
+  free (buffer);
076f82
+  xfclose (out);
076f82
+
076f82
+  return 0;
076f82
+}
076f82
+
076f82
+#define TEST_FUNCTION_ARGV do_test
076f82
+#include <support/test-driver.c>
076f82
diff --git a/elf/tst-audit19bmod.c b/elf/tst-audit19bmod.c
076f82
new file mode 100644
076f82
index 0000000000000000..9ffdcd8f3ffbc38e
076f82
--- /dev/null
076f82
+++ b/elf/tst-audit19bmod.c
076f82
@@ -0,0 +1,23 @@
076f82
+/* Extra module for tst-audit18b.
076f82
+   Copyright (C) 2021 Free Software Foundation, Inc.
076f82
+   This file is part of the GNU C Library.
076f82
+
076f82
+   The GNU C Library is free software; you can redistribute it and/or
076f82
+   modify it under the terms of the GNU Lesser General Public
076f82
+   License as published by the Free Software Foundation; either
076f82
+   version 2.1 of the License, or (at your option) any later version.
076f82
+
076f82
+   The GNU C Library is distributed in the hope that it will be useful,
076f82
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
076f82
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
076f82
+   Lesser General Public License for more details.
076f82
+
076f82
+   You should have received a copy of the GNU Lesser General Public
076f82
+   License along with the GNU C Library; if not, see
076f82
+   <https://www.gnu.org/licenses/>.  */
076f82
+
076f82
+int
076f82
+tst_audit18bmod1_func (void)
076f82
+{
076f82
+  return 10;
076f82
+}
076f82
diff --git a/elf/tst-auditmod19a.c b/elf/tst-auditmod19a.c
076f82
new file mode 100644
076f82
index 0000000000000000..f58204099457743d
076f82
--- /dev/null
076f82
+++ b/elf/tst-auditmod19a.c
076f82
@@ -0,0 +1,25 @@
076f82
+/* Audit module for tst-audit18a.
076f82
+   Copyright (C) 2021 Free Software Foundation, Inc.
076f82
+   This file is part of the GNU C Library.
076f82
+
076f82
+   The GNU C Library is free software; you can redistribute it and/or
076f82
+   modify it under the terms of the GNU Lesser General Public
076f82
+   License as published by the Free Software Foundation; either
076f82
+   version 2.1 of the License, or (at your option) any later version.
076f82
+
076f82
+   The GNU C Library is distributed in the hope that it will be useful,
076f82
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
076f82
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
076f82
+   Lesser General Public License for more details.
076f82
+
076f82
+   You should have received a copy of the GNU Lesser General Public
076f82
+   License along with the GNU C Library; if not, see
076f82
+   <https://www.gnu.org/licenses/>.  */
076f82
+
076f82
+#include <link.h>
076f82
+
076f82
+unsigned int
076f82
+la_version (unsigned int version)
076f82
+{
076f82
+  return LAV_CURRENT;
076f82
+}
076f82
diff --git a/elf/tst-auditmod19b.c b/elf/tst-auditmod19b.c
076f82
new file mode 100644
076f82
index 0000000000000000..e2248b2a75946746
076f82
--- /dev/null
076f82
+++ b/elf/tst-auditmod19b.c
076f82
@@ -0,0 +1,46 @@
076f82
+/* Audit module for tst-audit18b.
076f82
+   Copyright (C) 2021 Free Software Foundation, Inc.
076f82
+   This file is part of the GNU C Library.
076f82
+
076f82
+   The GNU C Library is free software; you can redistribute it and/or
076f82
+   modify it under the terms of the GNU Lesser General Public
076f82
+   License as published by the Free Software Foundation; either
076f82
+   version 2.1 of the License, or (at your option) any later version.
076f82
+
076f82
+   The GNU C Library is distributed in the hope that it will be useful,
076f82
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
076f82
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
076f82
+   Lesser General Public License for more details.
076f82
+
076f82
+   You should have received a copy of the GNU Lesser General Public
076f82
+   License along with the GNU C Library; if not, see
076f82
+   <https://www.gnu.org/licenses/>.  */
076f82
+
076f82
+#include <link.h>
076f82
+#include <string.h>
076f82
+#include <stdio.h>
076f82
+
076f82
+unsigned int
076f82
+la_version (unsigned int version)
076f82
+{
076f82
+  return LAV_CURRENT;
076f82
+}
076f82
+
076f82
+unsigned int
076f82
+la_objopen (struct link_map *map, Lmid_t lmid, uintptr_t *cookie)
076f82
+{
076f82
+  return LA_FLG_BINDTO | LA_FLG_BINDFROM;
076f82
+}
076f82
+
076f82
+uintptr_t
076f82
+#if __ELF_NATIVE_CLASS == 32
076f82
+la_symbind32 (Elf32_Sym *sym, unsigned int ndx, uintptr_t *refcook,
076f82
+	      uintptr_t *defcook, unsigned int *flags, const char *symname)
076f82
+#else
076f82
+la_symbind64 (Elf64_Sym *sym, unsigned int ndx, uintptr_t *refcook,
076f82
+	      uintptr_t *defcook, unsigned int *flags, const char *symname)
076f82
+#endif
076f82
+{
076f82
+  fprintf (stderr, "la_symbind: %s\n", symname);
076f82
+  return sym->st_value;
076f82
+}
076f82
diff --git a/include/link.h b/include/link.h
076f82
index 4dcf01d8aea90bc2..b3f160c278222b3c 100644
076f82
--- a/include/link.h
076f82
+++ b/include/link.h
076f82
@@ -363,8 +363,10 @@ struct auditstate
076f82
 
076f82
 #if __ELF_NATIVE_CLASS == 32
076f82
 # define symbind symbind32
076f82
+# define LA_SYMBIND "la_symbind32"
076f82
 #elif __ELF_NATIVE_CLASS == 64
076f82
 # define symbind symbind64
076f82
+# define LA_SYMBIND "la_symbind64"
076f82
 #else
076f82
 # error "__ELF_NATIVE_CLASS must be defined"
076f82
 #endif