446cf2
commit 3b856d093f5197637a5927c37d6c07dad8c86d45
446cf2
Author: Florian Weimer <fweimer@redhat.com>
446cf2
Date:   Tue Feb 12 13:36:56 2019 +0100
446cf2
446cf2
    elf: Ignore LD_AUDIT interfaces if la_version returns 0 [BZ #24122]
446cf2
    
446cf2
    This change moves the audit module loading and early notification into
446cf2
    separate functions out of dl_main.
446cf2
    
446cf2
    It restores the bug fix from commit
446cf2
    8e889c5da3c5981c5a46a93fec02de40131ac5a6  ("elf: Fix LD_AUDIT for
446cf2
    modules with invalid version (BZ#24122)") which was reverted in commit
446cf2
    83e6b59625f45db1eee93e5684091f740c52a083  ("[elf] Revert 8e889c5da3
446cf2
    (BZ#24122)").
446cf2
    
446cf2
    The actual bug fix is the separate error message for the case when
446cf2
    la_version returns zero.  The dynamic linker error message (which is
446cf2
    NULL in this case) is no longer used.  Based on the intended use of
446cf2
    version zero (ignore this module due to explicit request), the message
446cf2
    is only printed if debugging is enabled.
446cf2
446cf2
diff --git a/elf/rtld.c b/elf/rtld.c
446cf2
index 8bb5f548a0ff8eb4..375e0de8fa2e049e 100644
446cf2
--- a/elf/rtld.c
446cf2
+++ b/elf/rtld.c
446cf2
@@ -864,6 +864,205 @@ handle_preload_list (const char *preloadlist, struct link_map *main_map,
446cf2
   return npreloads;
446cf2
 }
446cf2
 
446cf2
+/* Called if the audit DSO cannot be used: if it does not have the
446cf2
+   appropriate interfaces, or it expects a more recent version library
446cf2
+   version than what the dynamic linker provides.  */
446cf2
+static void
446cf2
+unload_audit_module (struct link_map *map, int original_tls_idx)
446cf2
+{
446cf2
+#ifndef NDEBUG
446cf2
+  Lmid_t ns = map->l_ns;
446cf2
+#endif
446cf2
+  _dl_close (map);
446cf2
+
446cf2
+  /* Make sure the namespace has been cleared entirely.  */
446cf2
+  assert (GL(dl_ns)[ns]._ns_loaded == NULL);
446cf2
+  assert (GL(dl_ns)[ns]._ns_nloaded == 0);
446cf2
+
446cf2
+  GL(dl_tls_max_dtv_idx) = original_tls_idx;
446cf2
+}
446cf2
+
446cf2
+/* Called to print an error message if loading of an audit module
446cf2
+   failed.  */
446cf2
+static void
446cf2
+report_audit_module_load_error (const char *name, const char *err_str,
446cf2
+				bool malloced)
446cf2
+{
446cf2
+  _dl_error_printf ("\
446cf2
+ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
446cf2
+		    name, err_str);
446cf2
+  if (malloced)
446cf2
+    free ((char *) err_str);
446cf2
+}
446cf2
+
446cf2
+/* Load one audit module.  */
446cf2
+static void
446cf2
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
446cf2
+{
446cf2
+  int original_tls_idx = GL(dl_tls_max_dtv_idx);
446cf2
+
446cf2
+  struct dlmopen_args dlmargs;
446cf2
+  dlmargs.fname = name;
446cf2
+  dlmargs.map = NULL;
446cf2
+
446cf2
+  const char *objname;
446cf2
+  const char *err_str = NULL;
446cf2
+  bool malloced;
446cf2
+  _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit, &dlmargs);
446cf2
+  if (__glibc_unlikely (err_str != NULL))
446cf2
+    {
446cf2
+      report_audit_module_load_error (name, err_str, malloced);
446cf2
+      return;
446cf2
+    }
446cf2
+
446cf2
+  struct lookup_args largs;
446cf2
+  largs.name = "la_version";
446cf2
+  largs.map = dlmargs.map;
446cf2
+  _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
446cf2
+  if (__glibc_likely (err_str != NULL))
446cf2
+    {
446cf2
+      unload_audit_module (dlmargs.map, original_tls_idx);
446cf2
+      report_audit_module_load_error (name, err_str, malloced);
446cf2
+      return;
446cf2
+    }
446cf2
+
446cf2
+  unsigned int (*laversion) (unsigned int) = largs.result;
446cf2
+
446cf2
+ /* A null symbol indicates that something is very wrong with the
446cf2
+    loaded object because defined symbols are supposed to have a
446cf2
+    valid, non-null address.  */
446cf2
+  assert (laversion != NULL);
446cf2
+
446cf2
+  unsigned int lav = laversion (LAV_CURRENT);
446cf2
+  if (lav == 0)
446cf2
+    {
446cf2
+      /* Only print an error message if debugging because this can
446cf2
+	 happen deliberately.  */
446cf2
+      if (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
446cf2
+	_dl_debug_printf ("\
446cf2
+file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
446cf2
+			  dlmargs.map->l_name, dlmargs.map->l_ns);
446cf2
+      unload_audit_module (dlmargs.map, original_tls_idx);
446cf2
+      return;
446cf2
+    }
446cf2
+
446cf2
+  if (lav > LAV_CURRENT)
446cf2
+    {
446cf2
+      _dl_debug_printf ("\
446cf2
+ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
446cf2
+			name, lav, LAV_CURRENT);
446cf2
+      unload_audit_module (dlmargs.map, original_tls_idx);
446cf2
+      return;
446cf2
+    }
446cf2
+
446cf2
+  enum { naudit_ifaces = 8 };
446cf2
+  union
446cf2
+  {
446cf2
+    struct audit_ifaces ifaces;
446cf2
+    void (*fptr[naudit_ifaces]) (void);
446cf2
+  } *newp = malloc (sizeof (*newp));
446cf2
+  if (newp == NULL)
446cf2
+    _dl_fatal_printf ("Out of memory while loading audit modules\n");
446cf2
+
446cf2
+  /* Names of the auditing interfaces.  All in one
446cf2
+     long string.  */
446cf2
+  static const char audit_iface_names[] =
446cf2
+    "la_activity\0"
446cf2
+    "la_objsearch\0"
446cf2
+    "la_objopen\0"
446cf2
+    "la_preinit\0"
446cf2
+#if __ELF_NATIVE_CLASS == 32
446cf2
+    "la_symbind32\0"
446cf2
+#elif __ELF_NATIVE_CLASS == 64
446cf2
+    "la_symbind64\0"
446cf2
+#else
446cf2
+# error "__ELF_NATIVE_CLASS must be defined"
446cf2
+#endif
446cf2
+#define STRING(s) __STRING (s)
446cf2
+    "la_" STRING (ARCH_LA_PLTENTER) "\0"
446cf2
+    "la_" STRING (ARCH_LA_PLTEXIT) "\0"
446cf2
+    "la_objclose\0";
446cf2
+  unsigned int cnt = 0;
446cf2
+  const char *cp = audit_iface_names;
446cf2
+  do
446cf2
+    {
446cf2
+      largs.name = cp;
446cf2
+      _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
446cf2
+
446cf2
+      /* Store the pointer.  */
446cf2
+      if (err_str == NULL && largs.result != NULL)
446cf2
+	{
446cf2
+	  newp->fptr[cnt] = largs.result;
446cf2
+
446cf2
+	  /* The dynamic linker link map is statically allocated,
446cf2
+	     initialize the data now.  */
446cf2
+	  GL(dl_rtld_map).l_audit[cnt].cookie = (intptr_t) &GL(dl_rtld_map);
446cf2
+	}
446cf2
+      else
446cf2
+	newp->fptr[cnt] = NULL;
446cf2
+      ++cnt;
446cf2
+
446cf2
+      cp = rawmemchr (cp, '\0') + 1;
446cf2
+    }
446cf2
+  while (*cp != '\0');
446cf2
+  assert (cnt == naudit_ifaces);
446cf2
+
446cf2
+  /* Now append the new auditing interface to the list.  */
446cf2
+  newp->ifaces.next = NULL;
446cf2
+  if (*last_audit == NULL)
446cf2
+    *last_audit = GLRO(dl_audit) = &newp->ifaces;
446cf2
+  else
446cf2
+    *last_audit = (*last_audit)->next = &newp->ifaces;
446cf2
+  ++GLRO(dl_naudit);
446cf2
+
446cf2
+  /* Mark the DSO as being used for auditing.  */
446cf2
+  dlmargs.map->l_auditing = 1;
446cf2
+}
446cf2
+
446cf2
+/* Notify the the audit modules that the object MAP has already been
446cf2
+   loaded.  */
446cf2
+static void
446cf2
+notify_audit_modules_of_loaded_object (struct link_map *map)
446cf2
+{
446cf2
+  struct audit_ifaces *afct = GLRO(dl_audit);
446cf2
+  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
446cf2
+    {
446cf2
+      if (afct->objopen != NULL)
446cf2
+	{
446cf2
+	  map->l_audit[cnt].bindflags
446cf2
+	    = afct->objopen (map, LM_ID_BASE, &map->l_audit[cnt].cookie);
446cf2
+	  map->l_audit_any_plt |= map->l_audit[cnt].bindflags != 0;
446cf2
+	}
446cf2
+
446cf2
+      afct = afct->next;
446cf2
+    }
446cf2
+}
446cf2
+
446cf2
+/* Load all audit modules.  */
446cf2
+static void
446cf2
+load_audit_modules (struct link_map *main_map)
446cf2
+{
446cf2
+  struct audit_ifaces *last_audit = NULL;
446cf2
+  struct audit_list_iter al_iter;
446cf2
+  audit_list_iter_init (&al_iter);
446cf2
+
446cf2
+  while (true)
446cf2
+    {
446cf2
+      const char *name = audit_list_iter_next (&al_iter);
446cf2
+      if (name == NULL)
446cf2
+	break;
446cf2
+      load_audit_module (name, &last_audit);
446cf2
+    }
446cf2
+
446cf2
+  /* Notify audit modules of the initially loaded modules (the main
446cf2
+     program and the dynamic linker itself).  */
446cf2
+  if (GLRO(dl_naudit) > 0)
446cf2
+    {
446cf2
+      notify_audit_modules_of_loaded_object (main_map);
446cf2
+      notify_audit_modules_of_loaded_object (&GL(dl_rtld_map));
446cf2
+    }
446cf2
+}
446cf2
+
446cf2
 static void
446cf2
 dl_main (const ElfW(Phdr) *phdr,
446cf2
 	 ElfW(Word) phnum,
446cf2
@@ -1402,10 +1601,6 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
446cf2
   if (__glibc_unlikely (audit_list != NULL)
446cf2
       || __glibc_unlikely (audit_list_string != NULL))
446cf2
     {
446cf2
-      struct audit_ifaces *last_audit = NULL;
446cf2
-      struct audit_list_iter al_iter;
446cf2
-      audit_list_iter_init (&al_iter);
446cf2
-
446cf2
       /* Since we start using the auditing DSOs right away we need to
446cf2
 	 initialize the data structures now.  */
446cf2
       tcbp = init_tls ();
446cf2
@@ -1417,164 +1612,7 @@ ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
446cf2
       security_init ();
446cf2
       need_security_init = false;
446cf2
 
446cf2
-      while (true)
446cf2
-	{
446cf2
-	  const char *name = audit_list_iter_next (&al_iter);
446cf2
-	  if (name == NULL)
446cf2
-	    break;
446cf2
-
446cf2
-	  int tls_idx = GL(dl_tls_max_dtv_idx);
446cf2
-
446cf2
-	  /* Now it is time to determine the layout of the static TLS
446cf2
-	     block and allocate it for the initial thread.  Note that we
446cf2
-	     always allocate the static block, we never defer it even if
446cf2
-	     no DF_STATIC_TLS bit is set.  The reason is that we know
446cf2
-	     glibc will use the static model.  */
446cf2
-	  struct dlmopen_args dlmargs;
446cf2
-	  dlmargs.fname = name;
446cf2
-	  dlmargs.map = NULL;
446cf2
-
446cf2
-	  const char *objname;
446cf2
-	  const char *err_str = NULL;
446cf2
-	  bool malloced;
446cf2
-	  (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit,
446cf2
-				  &dlmargs);
446cf2
-	  if (__glibc_unlikely (err_str != NULL))
446cf2
-	    {
446cf2
-	    not_loaded:
446cf2
-	      _dl_error_printf ("\
446cf2
-ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
446cf2
-				name, err_str);
446cf2
-	      if (malloced)
446cf2
-		free ((char *) err_str);
446cf2
-	    }
446cf2
-	  else
446cf2
-	    {
446cf2
-	      struct lookup_args largs;
446cf2
-	      largs.name = "la_version";
446cf2
-	      largs.map = dlmargs.map;
446cf2
-
446cf2
-	      /* Check whether the interface version matches.  */
446cf2
-	      (void) _dl_catch_error (&objname, &err_str, &malloced,
446cf2
-				      lookup_doit, &largs);
446cf2
-
446cf2
-	      unsigned int (*laversion) (unsigned int);
446cf2
-	      unsigned int lav;
446cf2
-	      if  (err_str == NULL
446cf2
-		   && (laversion = largs.result) != NULL
446cf2
-		   && (lav = laversion (LAV_CURRENT)) > 0
446cf2
-		   && lav <= LAV_CURRENT)
446cf2
-		{
446cf2
-		  /* Allocate structure for the callback function pointers.
446cf2
-		     This call can never fail.  */
446cf2
-		  union
446cf2
-		  {
446cf2
-		    struct audit_ifaces ifaces;
446cf2
-#define naudit_ifaces 8
446cf2
-		    void (*fptr[naudit_ifaces]) (void);
446cf2
-		  } *newp = malloc (sizeof (*newp));
446cf2
-
446cf2
-		  /* Names of the auditing interfaces.  All in one
446cf2
-		     long string.  */
446cf2
-		  static const char audit_iface_names[] =
446cf2
-		    "la_activity\0"
446cf2
-		    "la_objsearch\0"
446cf2
-		    "la_objopen\0"
446cf2
-		    "la_preinit\0"
446cf2
-#if __ELF_NATIVE_CLASS == 32
446cf2
-		    "la_symbind32\0"
446cf2
-#elif __ELF_NATIVE_CLASS == 64
446cf2
-		    "la_symbind64\0"
446cf2
-#else
446cf2
-# error "__ELF_NATIVE_CLASS must be defined"
446cf2
-#endif
446cf2
-#define STRING(s) __STRING (s)
446cf2
-		    "la_" STRING (ARCH_LA_PLTENTER) "\0"
446cf2
-		    "la_" STRING (ARCH_LA_PLTEXIT) "\0"
446cf2
-		    "la_objclose\0";
446cf2
-		  unsigned int cnt = 0;
446cf2
-		  const char *cp = audit_iface_names;
446cf2
-		  do
446cf2
-		    {
446cf2
-		      largs.name = cp;
446cf2
-		      (void) _dl_catch_error (&objname, &err_str, &malloced,
446cf2
-					      lookup_doit, &largs);
446cf2
-
446cf2
-		      /* Store the pointer.  */
446cf2
-		      if (err_str == NULL && largs.result != NULL)
446cf2
-			{
446cf2
-			  newp->fptr[cnt] = largs.result;
446cf2
-
446cf2
-			  /* The dynamic linker link map is statically
446cf2
-			     allocated, initialize the data now.   */
446cf2
-			  GL(dl_rtld_map).l_audit[cnt].cookie
446cf2
-			    = (intptr_t) &GL(dl_rtld_map);
446cf2
-			}
446cf2
-		      else
446cf2
-			newp->fptr[cnt] = NULL;
446cf2
-		      ++cnt;
446cf2
-
446cf2
-		      cp = (char *) rawmemchr (cp, '\0') + 1;
446cf2
-		    }
446cf2
-		  while (*cp != '\0');
446cf2
-		  assert (cnt == naudit_ifaces);
446cf2
-
446cf2
-		  /* Now append the new auditing interface to the list.  */
446cf2
-		  newp->ifaces.next = NULL;
446cf2
-		  if (last_audit == NULL)
446cf2
-		    last_audit = GLRO(dl_audit) = &newp->ifaces;
446cf2
-		  else
446cf2
-		    last_audit = last_audit->next = &newp->ifaces;
446cf2
-		  ++GLRO(dl_naudit);
446cf2
-
446cf2
-		  /* Mark the DSO as being used for auditing.  */
446cf2
-		  dlmargs.map->l_auditing = 1;
446cf2
-		}
446cf2
-	      else
446cf2
-		{
446cf2
-		  /* We cannot use the DSO, it does not have the
446cf2
-		     appropriate interfaces or it expects something
446cf2
-		     more recent.  */
446cf2
-#ifndef NDEBUG
446cf2
-		  Lmid_t ns = dlmargs.map->l_ns;
446cf2
-#endif
446cf2
-		  _dl_close (dlmargs.map);
446cf2
-
446cf2
-		  /* Make sure the namespace has been cleared entirely.  */
446cf2
-		  assert (GL(dl_ns)[ns]._ns_loaded == NULL);
446cf2
-		  assert (GL(dl_ns)[ns]._ns_nloaded == 0);
446cf2
-
446cf2
-		  GL(dl_tls_max_dtv_idx) = tls_idx;
446cf2
-		  goto not_loaded;
446cf2
-		}
446cf2
-	    }
446cf2
-	}
446cf2
-
446cf2
-      /* If we have any auditing modules, announce that we already
446cf2
-	 have two objects loaded.  */
446cf2
-      if (__glibc_unlikely (GLRO(dl_naudit) > 0))
446cf2
-	{
446cf2
-	  struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) };
446cf2
-
446cf2
-	  for (unsigned int outer = 0; outer < 2; ++outer)
446cf2
-	    {
446cf2
-	      struct audit_ifaces *afct = GLRO(dl_audit);
446cf2
-	      for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
446cf2
-		{
446cf2
-		  if (afct->objopen != NULL)
446cf2
-		    {
446cf2
-		      ls[outer]->l_audit[cnt].bindflags
446cf2
-			= afct->objopen (ls[outer], LM_ID_BASE,
446cf2
-					 &ls[outer]->l_audit[cnt].cookie);
446cf2
-
446cf2
-		      ls[outer]->l_audit_any_plt
446cf2
-			|= ls[outer]->l_audit[cnt].bindflags != 0;
446cf2
-		    }
446cf2
-
446cf2
-		  afct = afct->next;
446cf2
-		}
446cf2
-	    }
446cf2
-	}
446cf2
+      load_audit_modules (main_map);
446cf2
     }
446cf2
 
446cf2
   /* Keep track of the currently loaded modules to count how many