elf: Ignore LD_AUDIT interfaces if la_version returns 0 [BZ #24122]
[platform/upstream/glibc.git] / elf / rtld.c
index 8c732ad..c1cc1b0 100644 (file)
@@ -1,5 +1,5 @@
 /* Run time dynamic linker.
-   Copyright (C) 1995-2018 Free Software Foundation, Inc.
+   Copyright (C) 1995-2019 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
@@ -38,6 +38,7 @@
 #include <dl-cache.h>
 #include <dl-osinfo.h>
 #include <dl-procinfo.h>
+#include <dl-prop.h>
 #include <tls.h>
 #include <stap-probe.h>
 #include <stackinfo.h>
@@ -825,15 +826,18 @@ static const char *library_path attribute_relro;
 static const char *preloadlist attribute_relro;
 /* Nonzero if information about versions has to be printed.  */
 static int version_info attribute_relro;
+/* The preload list passed as a command argument.  */
+static const char *preloadarg attribute_relro;
 
 /* The LD_PRELOAD environment variable gives list of libraries
    separated by white space or colons that are loaded before the
    executable's dependencies and prepended to the global scope list.
    (If the binary is running setuid all elements containing a '/' are
    ignored since it is insecure.)  Return the number of preloads
-   performed.  */
+   performed.   Ditto for --preload command argument.  */
 unsigned int
-handle_ld_preload (const char *preloadlist, struct link_map *main_map)
+handle_preload_list (const char *preloadlist, struct link_map *main_map,
+                    const char *where)
 {
   unsigned int npreloads = 0;
   const char *p = preloadlist;
@@ -857,11 +861,210 @@ handle_ld_preload (const char *preloadlist, struct link_map *main_map)
        ++p;
 
       if (dso_name_valid_for_suid (fname))
-       npreloads += do_preload (fname, main_map, "LD_PRELOAD");
+       npreloads += do_preload (fname, main_map, where);
     }
   return npreloads;
 }
 
+/* Called if the audit DSO cannot be used: if it does not have the
+   appropriate interfaces, or it expects a more recent version library
+   version than what the dynamic linker provides.  */
+static void
+unload_audit_module (struct link_map *map, int original_tls_idx)
+{
+#ifndef NDEBUG
+  Lmid_t ns = map->l_ns;
+#endif
+  _dl_close (map);
+
+  /* Make sure the namespace has been cleared entirely.  */
+  assert (GL(dl_ns)[ns]._ns_loaded == NULL);
+  assert (GL(dl_ns)[ns]._ns_nloaded == 0);
+
+  GL(dl_tls_max_dtv_idx) = original_tls_idx;
+}
+
+/* Called to print an error message if loading of an audit module
+   failed.  */
+static void
+report_audit_module_load_error (const char *name, const char *err_str,
+                               bool malloced)
+{
+  _dl_error_printf ("\
+ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
+                   name, err_str);
+  if (malloced)
+    free ((char *) err_str);
+}
+
+/* Load one audit module.  */
+static void
+load_audit_module (const char *name, struct audit_ifaces **last_audit)
+{
+  int original_tls_idx = GL(dl_tls_max_dtv_idx);
+
+  struct dlmopen_args dlmargs;
+  dlmargs.fname = name;
+  dlmargs.map = NULL;
+
+  const char *objname;
+  const char *err_str = NULL;
+  bool malloced;
+  _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit, &dlmargs);
+  if (__glibc_unlikely (err_str != NULL))
+    {
+      report_audit_module_load_error (name, err_str, malloced);
+      return;
+    }
+
+  struct lookup_args largs;
+  largs.name = "la_version";
+  largs.map = dlmargs.map;
+  _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
+  if (__glibc_likely (err_str != NULL))
+    {
+      unload_audit_module (dlmargs.map, original_tls_idx);
+      report_audit_module_load_error (name, err_str, malloced);
+      return;
+    }
+
+  unsigned int (*laversion) (unsigned int) = largs.result;
+
+ /* A null symbol indicates that something is very wrong with the
+    loaded object because defined symbols are supposed to have a
+    valid, non-null address.  */
+  assert (laversion != NULL);
+
+  unsigned int lav = laversion (LAV_CURRENT);
+  if (lav == 0)
+    {
+      /* Only print an error message if debugging because this can
+        happen deliberately.  */
+      if (GLRO(dl_debug_mask) & DL_DEBUG_FILES)
+       _dl_debug_printf ("\
+file=%s [%lu]; audit interface function la_version returned zero; ignored.\n",
+                         dlmargs.map->l_name, dlmargs.map->l_ns);
+      unload_audit_module (dlmargs.map, original_tls_idx);
+      return;
+    }
+
+  if (lav > LAV_CURRENT)
+    {
+      _dl_debug_printf ("\
+ERROR: audit interface '%s' requires version %d (maximum supported version %d); ignored.\n",
+                       name, lav, LAV_CURRENT);
+      unload_audit_module (dlmargs.map, original_tls_idx);
+      return;
+    }
+
+  enum { naudit_ifaces = 8 };
+  union
+  {
+    struct audit_ifaces ifaces;
+    void (*fptr[naudit_ifaces]) (void);
+  } *newp = malloc (sizeof (*newp));
+  if (newp == NULL)
+    _dl_fatal_printf ("Out of memory while loading audit modules\n");
+
+  /* Names of the auditing interfaces.  All in one
+     long string.  */
+  static const char audit_iface_names[] =
+    "la_activity\0"
+    "la_objsearch\0"
+    "la_objopen\0"
+    "la_preinit\0"
+#if __ELF_NATIVE_CLASS == 32
+    "la_symbind32\0"
+#elif __ELF_NATIVE_CLASS == 64
+    "la_symbind64\0"
+#else
+# error "__ELF_NATIVE_CLASS must be defined"
+#endif
+#define STRING(s) __STRING (s)
+    "la_" STRING (ARCH_LA_PLTENTER) "\0"
+    "la_" STRING (ARCH_LA_PLTEXIT) "\0"
+    "la_objclose\0";
+  unsigned int cnt = 0;
+  const char *cp = audit_iface_names;
+  do
+    {
+      largs.name = cp;
+      _dl_catch_error (&objname, &err_str, &malloced, lookup_doit, &largs);
+
+      /* Store the pointer.  */
+      if (err_str == NULL && largs.result != NULL)
+       {
+         newp->fptr[cnt] = largs.result;
+
+         /* The dynamic linker link map is statically allocated,
+            initialize the data now.  */
+         GL(dl_rtld_map).l_audit[cnt].cookie = (intptr_t) &GL(dl_rtld_map);
+       }
+      else
+       newp->fptr[cnt] = NULL;
+      ++cnt;
+
+      cp = rawmemchr (cp, '\0') + 1;
+    }
+  while (*cp != '\0');
+  assert (cnt == naudit_ifaces);
+
+  /* Now append the new auditing interface to the list.  */
+  newp->ifaces.next = NULL;
+  if (*last_audit == NULL)
+    *last_audit = GLRO(dl_audit) = &newp->ifaces;
+  else
+    *last_audit = (*last_audit)->next = &newp->ifaces;
+  ++GLRO(dl_naudit);
+
+  /* Mark the DSO as being used for auditing.  */
+  dlmargs.map->l_auditing = 1;
+}
+
+/* Notify the the audit modules that the object MAP has already been
+   loaded.  */
+static void
+notify_audit_modules_of_loaded_object (struct link_map *map)
+{
+  struct audit_ifaces *afct = GLRO(dl_audit);
+  for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
+    {
+      if (afct->objopen != NULL)
+       {
+         map->l_audit[cnt].bindflags
+           = afct->objopen (map, LM_ID_BASE, &map->l_audit[cnt].cookie);
+         map->l_audit_any_plt |= map->l_audit[cnt].bindflags != 0;
+       }
+
+      afct = afct->next;
+    }
+}
+
+/* Load all audit modules.  */
+static void
+load_audit_modules (struct link_map *main_map)
+{
+  struct audit_ifaces *last_audit = NULL;
+  struct audit_list_iter al_iter;
+  audit_list_iter_init (&al_iter);
+
+  while (true)
+    {
+      const char *name = audit_list_iter_next (&al_iter);
+      if (name == NULL)
+       break;
+      load_audit_module (name, &last_audit);
+    }
+
+  /* Notify audit modules of the initially loaded modules (the main
+     program and the dynamic linker itself).  */
+  if (GLRO(dl_naudit) > 0)
+    {
+      notify_audit_modules_of_loaded_object (main_map);
+      notify_audit_modules_of_loaded_object (&GL(dl_rtld_map));
+    }
+}
+
 static void
 dl_main (const ElfW(Phdr) *phdr,
         ElfW(Word) phnum,
@@ -977,6 +1180,13 @@ dl_main (const ElfW(Phdr) *phdr,
            _dl_argc -= 2;
            _dl_argv += 2;
          }
+       else if (! strcmp (_dl_argv[1], "--preload") && _dl_argc > 2)
+         {
+           preloadarg = _dl_argv[2];
+           _dl_skip_args += 2;
+           _dl_argc -= 2;
+           _dl_argv += 2;
+         }
        else
          break;
 
@@ -1005,7 +1215,8 @@ of this helper program; chances are you did not intend to run this program.\n\
                        variable LD_LIBRARY_PATH\n\
   --inhibit-rpath LIST  ignore RUNPATH and RPATH information in object names\n\
                        in LIST\n\
-  --audit LIST          use objects named in LIST as auditors\n");
+  --audit LIST          use objects named in LIST as auditors\n\
+  --preload LIST        preload objects named in LIST\n");
 
       ++_dl_skip_args;
       --_dl_argc;
@@ -1241,6 +1452,12 @@ of this helper program; chances are you did not intend to run this program.\n\
        main_map->l_relro_addr = ph->p_vaddr;
        main_map->l_relro_size = ph->p_memsz;
        break;
+
+      case PT_NOTE:
+       if (_rtld_process_pt_note (main_map, ph))
+         _dl_error_printf ("\
+ERROR: '%s': cannot process note segment.\n", _dl_argv[0]);
+       break;
       }
 
   /* Adjust the address of the TLS initialization image in case
@@ -1388,10 +1605,6 @@ of this helper program; chances are you did not intend to run this program.\n\
   if (__glibc_unlikely (audit_list != NULL)
       || __glibc_unlikely (audit_list_string != NULL))
     {
-      struct audit_ifaces *last_audit = NULL;
-      struct audit_list_iter al_iter;
-      audit_list_iter_init (&al_iter);
-
       /* Since we start using the auditing DSOs right away we need to
         initialize the data structures now.  */
       tcbp = init_tls ();
@@ -1403,164 +1616,7 @@ of this helper program; chances are you did not intend to run this program.\n\
       security_init ();
       need_security_init = false;
 
-      while (true)
-       {
-         const char *name = audit_list_iter_next (&al_iter);
-         if (name == NULL)
-           break;
-
-         int tls_idx = GL(dl_tls_max_dtv_idx);
-
-         /* Now it is time to determine the layout of the static TLS
-            block and allocate it for the initial thread.  Note that we
-            always allocate the static block, we never defer it even if
-            no DF_STATIC_TLS bit is set.  The reason is that we know
-            glibc will use the static model.  */
-         struct dlmopen_args dlmargs;
-         dlmargs.fname = name;
-         dlmargs.map = NULL;
-
-         const char *objname;
-         const char *err_str = NULL;
-         bool malloced;
-         (void) _dl_catch_error (&objname, &err_str, &malloced, dlmopen_doit,
-                                 &dlmargs);
-         if (__glibc_unlikely (err_str != NULL))
-           {
-           not_loaded:
-             _dl_error_printf ("\
-ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
-                               name, err_str);
-             if (malloced)
-               free ((char *) err_str);
-           }
-         else
-           {
-             struct lookup_args largs;
-             largs.name = "la_version";
-             largs.map = dlmargs.map;
-
-             /* Check whether the interface version matches.  */
-             (void) _dl_catch_error (&objname, &err_str, &malloced,
-                                     lookup_doit, &largs);
-
-             unsigned int (*laversion) (unsigned int);
-             unsigned int lav;
-             if  (err_str == NULL
-                  && (laversion = largs.result) != NULL
-                  && (lav = laversion (LAV_CURRENT)) > 0
-                  && lav <= LAV_CURRENT)
-               {
-                 /* Allocate structure for the callback function pointers.
-                    This call can never fail.  */
-                 union
-                 {
-                   struct audit_ifaces ifaces;
-#define naudit_ifaces 8
-                   void (*fptr[naudit_ifaces]) (void);
-                 } *newp = malloc (sizeof (*newp));
-
-                 /* Names of the auditing interfaces.  All in one
-                    long string.  */
-                 static const char audit_iface_names[] =
-                   "la_activity\0"
-                   "la_objsearch\0"
-                   "la_objopen\0"
-                   "la_preinit\0"
-#if __ELF_NATIVE_CLASS == 32
-                   "la_symbind32\0"
-#elif __ELF_NATIVE_CLASS == 64
-                   "la_symbind64\0"
-#else
-# error "__ELF_NATIVE_CLASS must be defined"
-#endif
-#define STRING(s) __STRING (s)
-                   "la_" STRING (ARCH_LA_PLTENTER) "\0"
-                   "la_" STRING (ARCH_LA_PLTEXIT) "\0"
-                   "la_objclose\0";
-                 unsigned int cnt = 0;
-                 const char *cp = audit_iface_names;
-                 do
-                   {
-                     largs.name = cp;
-                     (void) _dl_catch_error (&objname, &err_str, &malloced,
-                                             lookup_doit, &largs);
-
-                     /* Store the pointer.  */
-                     if (err_str == NULL && largs.result != NULL)
-                       {
-                         newp->fptr[cnt] = largs.result;
-
-                         /* The dynamic linker link map is statically
-                            allocated, initialize the data now.   */
-                         GL(dl_rtld_map).l_audit[cnt].cookie
-                           = (intptr_t) &GL(dl_rtld_map);
-                       }
-                     else
-                       newp->fptr[cnt] = NULL;
-                     ++cnt;
-
-                     cp = (char *) rawmemchr (cp, '\0') + 1;
-                   }
-                 while (*cp != '\0');
-                 assert (cnt == naudit_ifaces);
-
-                 /* Now append the new auditing interface to the list.  */
-                 newp->ifaces.next = NULL;
-                 if (last_audit == NULL)
-                   last_audit = GLRO(dl_audit) = &newp->ifaces;
-                 else
-                   last_audit = last_audit->next = &newp->ifaces;
-                 ++GLRO(dl_naudit);
-
-                 /* Mark the DSO as being used for auditing.  */
-                 dlmargs.map->l_auditing = 1;
-               }
-             else
-               {
-                 /* We cannot use the DSO, it does not have the
-                    appropriate interfaces or it expects something
-                    more recent.  */
-#ifndef NDEBUG
-                 Lmid_t ns = dlmargs.map->l_ns;
-#endif
-                 _dl_close (dlmargs.map);
-
-                 /* Make sure the namespace has been cleared entirely.  */
-                 assert (GL(dl_ns)[ns]._ns_loaded == NULL);
-                 assert (GL(dl_ns)[ns]._ns_nloaded == 0);
-
-                 GL(dl_tls_max_dtv_idx) = tls_idx;
-                 goto not_loaded;
-               }
-           }
-       }
-
-      /* If we have any auditing modules, announce that we already
-        have two objects loaded.  */
-      if (__glibc_unlikely (GLRO(dl_naudit) > 0))
-       {
-         struct link_map *ls[2] = { main_map, &GL(dl_rtld_map) };
-
-         for (unsigned int outer = 0; outer < 2; ++outer)
-           {
-             struct audit_ifaces *afct = GLRO(dl_audit);
-             for (unsigned int cnt = 0; cnt < GLRO(dl_naudit); ++cnt)
-               {
-                 if (afct->objopen != NULL)
-                   {
-                     ls[outer]->l_audit[cnt].bindflags
-                       = afct->objopen (ls[outer], LM_ID_BASE,
-                                        &ls[outer]->l_audit[cnt].cookie);
-
-                     ls[outer]->l_audit_any_plt
-                       |= ls[outer]->l_audit[cnt].bindflags != 0;
-                   }
-
-                 afct = afct->next;
-               }
-           }
-       }
+      load_audit_modules (main_map);
     }
 
   /* Keep track of the currently loaded modules to count how many
@@ -1613,7 +1669,16 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
   if (__glibc_unlikely (preloadlist != NULL))
     {
       HP_TIMING_NOW (start);
-      npreloads += handle_ld_preload (preloadlist, main_map);
+      npreloads += handle_preload_list (preloadlist, main_map, "LD_PRELOAD");
+      HP_TIMING_NOW (stop);
+      HP_TIMING_DIFF (diff, start, stop);
+      HP_TIMING_ACCUM_NT (load_time, diff);
+    }
+
+  if (__glibc_unlikely (preloadarg != NULL))
+    {
+      HP_TIMING_NOW (start);
+      npreloads += handle_preload_list (preloadarg, main_map, "--preload");
       HP_TIMING_NOW (stop);
       HP_TIMING_DIFF (diff, start, stop);
       HP_TIMING_ACCUM_NT (load_time, diff);
@@ -2110,6 +2175,8 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
        _dl_show_scope (l, 0);
     }
 
+  _rtld_main_check (main_map, _dl_argv[0]);
+
   if (prelinked)
     {
       if (main_map->l_info [ADDRIDX (DT_GNU_CONFLICT)] != NULL)
@@ -2709,8 +2776,10 @@ print_statistics (hp_timing_t *rtld_total_timep)
        {
        case 3:
          *wp++ = *cp++;
+         /* Fall through.  */
        case 2:
          *wp++ = *cp++;
+         /* Fall through.  */
        case 1:
          *wp++ = '.';
          *wp++ = *cp++;
@@ -2772,8 +2841,10 @@ print_statistics (hp_timing_t *rtld_total_timep)
        {
        case 3:
          *wp++ = *cp++;
+         /* Fall through.  */
        case 2:
          *wp++ = *cp++;
+         /* Fall through.  */
        case 1:
          *wp++ = '.';
          *wp++ = *cp++;