]> git.ipfire.org Git - thirdparty/glibc.git/commitdiff
ld.so: Examine GLRO to detect inactive loader [BZ #20204]
authorFlorian Weimer <fweimer@redhat.com>
Mon, 18 Dec 2017 19:04:13 +0000 (20:04 +0100)
committerFlorian Weimer <fweimer@redhat.com>
Mon, 18 Dec 2017 19:04:13 +0000 (20:04 +0100)
GLRO (_rtld_global_ro) is read-only after initialization and can
therefore not be patched at run time, unlike the hook table addresses
and their contents, so this is a desirable hardening feature.

The hooks are only needed if ld.so has not been initialized, and this
happens only after static dlopen (dlmopen uses a single ld.so object
across all namespaces).

Reviewed-by: Carlos O'Donell <carlos@redhat.com>
15 files changed:
ChangeLog
dlfcn/dladdr.c
dlfcn/dladdr1.c
dlfcn/dlclose.c
dlfcn/dlerror.c
dlfcn/dlinfo.c
dlfcn/dlmopen.c
dlfcn/dlopen.c
dlfcn/dlopenold.c
dlfcn/dlsym.c
dlfcn/dlvsym.c
elf/dl-libc.c
elf/rtld.c
libio/vtables.c
sysdeps/generic/ldsodefs.h

index c5ee8fc91ac6bad9585a65477ceed4fbf6189de2..a46375fd568429ecfa8a49119733d4d815365f59 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,25 @@
+2017-12-18  Florian Weimer  <fweimer@redhat.com>
+
+       [BZ #20204]
+       ld.so: Harden dl-libc/libdl hooks.
+       * sysdeps/generic/ldsodefs.h (_dl_init_all_dirs): Update comment.
+       (rtld_active): New function.
+       * dlfcn/dladdr.c (__dladdr): Call it.
+       * dlfcn/dladdr1.c (__dladdr1): Likewise.
+       * dlfcn/dlclose.c (__dlcose): Likewise.
+       * dlfcn/dlerror.c (__dlerror): Likewise.
+       * dlfcn/dlinfo.c (__dlinfo): Likewise.
+       * dlfcn/dlmopen.c (__dlmopen): Likewise.
+       * dlfcn/dlopen.c (__dlopen): Likewise.
+       * dlfcn/dlopenold.c (__dlopen_nocheck): Likewise.
+       * dlfcn/dlsym.c (__dlsym): Likewise.
+       * dlfcn/dlvsym.c (__dlvsym): Likewise.
+       * libio/vtables.c (_IO_vtable_check): Likewise.
+       * elf/dl-libc.c (__libc_dlopen_mode, __libc_dlsym)
+       (__libc_dlclose): Likewise.
+       * elf/rtld.c (dl_main): Update comment on the _dl_init_all_dirs
+       assignment.
+
 2017-12-18  Joseph Myers  <joseph@codesourcery.com>
 
        [BZ #22446]
index 1753434160717f01d3bc56844b3b48560655d047..1bebd00240caf9e6786465c130548bf075f0420e 100644 (file)
@@ -17,6 +17,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <dlfcn.h>
+#include <ldsodefs.h>
 
 #if !defined SHARED && IS_IN (libdl)
 
@@ -32,7 +33,7 @@ int
 __dladdr (const void *address, Dl_info *info)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dladdr (address, info);
 # endif
   return _dl_addr (address, info, NULL, NULL);
index a19f9fdea2e15b21a3f428c1e043b968e24a15ef..901cf43f6bfb2521eb1602bd0dacf95baaf9519c 100644 (file)
@@ -17,6 +17,7 @@
    <http://www.gnu.org/licenses/>.  */
 
 #include <dlfcn.h>
+#include <ldsodefs.h>
 
 #if !defined SHARED && IS_IN (libdl)
 
@@ -32,7 +33,7 @@ int
 __dladdr1 (const void *address, Dl_info *info, void **extra, int flags)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dladdr1 (address, info, extra, flags);
 # endif
 
index da66e20488742d248df73e821e26023a5e71e4ce..223887d3383c41401abeb647ca9aec0a10a6dc1c 100644 (file)
@@ -39,7 +39,7 @@ int
 __dlclose (void *handle)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlclose (handle);
 # endif
 
index fb5012ee854a7c924d62d83c41057b4e961a1236..b33c05095a29ad098307249df55c68c3d2d47a99 100644 (file)
@@ -63,7 +63,7 @@ __dlerror (void)
   struct dl_action_result *result;
 
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlerror ();
 # endif
 
index a34e947ed36005d9ccf20b8849fa5408515c667d..b0112574684d6c515a648c27ff59f259e8f9fd8e 100644 (file)
@@ -111,7 +111,7 @@ int
 __dlinfo (void *handle, int request, void *arg DL_CALLER_DECL)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlinfo (handle, request, arg,
                                DL_CALLER);
 # endif
index 07d59ade3025900bdc196e4775b7cdc8131a1448..58f88bb7c6e97e718e9baa0a6f032aaa88f269c7 100644 (file)
@@ -79,7 +79,7 @@ void *
 __dlmopen (Lmid_t nsid, const char *file, int mode DL_CALLER_DECL)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlmopen (nsid, file, mode, RETURN_ADDRESS (0));
 # endif
 
index 22120655d2fcbb3c6b6136690dc211dc9bb9b0c7..73651a8430e9320b5f6d13c83d1e1439f4c1b3b0 100644 (file)
@@ -74,7 +74,7 @@ void *
 __dlopen (const char *file, int mode DL_CALLER_DECL)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlopen (file, mode, DL_CALLER);
 # endif
 
index a3db5007058f4737303b6ce441a0d122cacd9593..d899c4e89017d4189ea9536462a418a47b47535e 100644 (file)
@@ -70,7 +70,7 @@ __dlopen_nocheck (const char *file, int mode)
     mode |= RTLD_LAZY;
   args.mode = mode;
 
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlopen (file, mode, RETURN_ADDRESS (0));
 
   return _dlerror_run (dlopen_doit, &args) ? NULL : args.new;
index 7976c5f75c4c7f03003bbda65014a10cc97f2bc4..19733a0f19c92e65ada0be8e5a48e6a201c27578 100644 (file)
@@ -55,7 +55,7 @@ void *
 __dlsym (void *handle, const char *name DL_CALLER_DECL)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlsym (handle, name, DL_CALLER);
 # endif
 
index 5ed220b77c5da53ea5afb31652636534c4ad0313..ad46b65023563437e2b2686d36abcbb561f05db3 100644 (file)
@@ -58,7 +58,7 @@ __dlvsym (void *handle, const char *name, const char *version_str
          DL_CALLER_DECL)
 {
 # ifdef SHARED
-  if (__glibc_unlikely (_dlfcn_hook != NULL))
+  if (!rtld_active ())
     return _dlfcn_hook->dlvsym (handle, name, version_str, DL_CALLER);
 # endif
 
index bd3c18d20ffd529c095d930c0ae8fd056b7f0896..7d9a8948f34d4723b152f73cc1260a28cb7994e9 100644 (file)
@@ -157,7 +157,7 @@ __libc_dlopen_mode (const char *name, int mode)
   args.caller_dlopen = RETURN_ADDRESS (0);
 
 #ifdef SHARED
-  if (__glibc_unlikely (_dl_open_hook != NULL))
+  if (!rtld_active ())
     return _dl_open_hook->dlopen_mode (name, mode);
   return (dlerror_run (do_dlopen, &args) ? NULL : (void *) args.map);
 #else
@@ -203,7 +203,7 @@ __libc_dlsym (void *map, const char *name)
   args.name = name;
 
 #ifdef SHARED
-  if (__glibc_unlikely (_dl_open_hook != NULL))
+  if (!rtld_active ())
     return _dl_open_hook->dlsym (map, name);
 #endif
   return (dlerror_run (do_dlsym, &args) ? NULL
@@ -215,7 +215,7 @@ int
 __libc_dlclose (void *map)
 {
 #ifdef SHARED
-  if (__glibc_unlikely (_dl_open_hook != NULL))
+  if (!rtld_active ())
     return _dl_open_hook->dlclose (map);
 #endif
   return dlerror_run (do_dlclose, map);
index cfd3729b8e7120d7f48c851deae7b6563a8df95e..c01b7e36416050b3d56190ccaffdfefeaa7ddc3a 100644 (file)
@@ -2096,7 +2096,9 @@ ERROR: ld.so: object '%s' cannot be loaded as audit interface: %s; ignored.\n",
   GLRO(dl_initial_searchlist) = *GL(dl_ns)[LM_ID_BASE]._ns_main_searchlist;
 
   /* Remember the last search directory added at startup, now that
-     malloc will no longer be the one from dl-minimal.c.  */
+     malloc will no longer be the one from dl-minimal.c.  As a side
+     effect, this marks ld.so as initialized, so that the rtld_active
+     function returns true from now on.  */
   GLRO(dl_init_all_dirs) = GL(dl_all_dirs);
 
   /* Print scope information.  */
index 41b48db98c71c84a2305a7400d56e5e7c6992343..4d4afa2efc412b0f6ebe3766290b603f9b031c95 100644 (file)
@@ -19,6 +19,7 @@
 #include <dlfcn.h>
 #include <libioP.h>
 #include <stdio.h>
+#include <ldsodefs.h>
 
 #ifdef SHARED
 
@@ -54,7 +55,7 @@ _IO_vtable_check (void)
   {
     Dl_info di;
     struct link_map *l;
-    if (_dl_open_hook != NULL
+    if (!rtld_active ()
         || (_dl_addr (_IO_vtable_check, &di, &l, NULL) != 0
             && l->l_ns != LM_ID_BASE))
       return;
index 196513851f59442196b7a59218371308ca2c74b5..658a4f20b4d5dea945c2b5dd7ef3a1df55c1c32c 100644 (file)
@@ -558,7 +558,11 @@ struct rtld_global_ro
   /* Map of shared object to be prelink traced.  */
   EXTERN struct link_map *_dl_trace_prelink_map;
 
-  /* All search directories defined at startup.  */
+  /* All search directories defined at startup.  This is assigned a
+     non-NULL pointer by the ld.so startup code (after initialization
+     to NULL), so this can also serve as an indicator whether a copy
+     of ld.so is initialized and active.  See the rtld_active function
+     below.  */
   EXTERN struct r_search_path_elem *_dl_init_all_dirs;
 
 #ifdef NEED_DL_SYSINFO
@@ -1144,6 +1148,20 @@ extern void _dl_non_dynamic_init (void)
 extern void _dl_aux_init (ElfW(auxv_t) *av)
      attribute_hidden;
 
+/* Return true if the ld.so copy in this namespace is actually active
+   and working.  If false, the dl_open/dlfcn hooks have to be used to
+   call into the outer dynamic linker (which happens after static
+   dlopen).  */
+#ifdef SHARED
+static inline bool
+rtld_active (void)
+{
+  /* The default-initialized variable does not have a non-zero
+     dl_init_all_dirs member, so this allows us to recognize an
+     initialized and active ld.so copy.  */
+  return GLRO(dl_init_all_dirs) != NULL;
+}
+#endif
 
 __END_DECLS