8a8cfb
commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23
8a8cfb
Author: Florian Weimer <fweimer@redhat.com>
8a8cfb
Date:   Thu Oct 31 19:30:19 2019 +0100
8a8cfb
8a8cfb
    Block signals during the initial part of dlopen
8a8cfb
    
8a8cfb
    Lazy binding in a signal handler that interrupts a dlopen sees
8a8cfb
    intermediate dynamic linker state.  This has likely been always
8a8cfb
    unsafe, but with the new pending NODELETE state, this is clearly
8a8cfb
    incorrect.  Other threads are excluded via the loader lock, but the
8a8cfb
    current thread is not.  Blocking signals until right before ELF
8a8cfb
    constructors run is the safe thing to do.
8a8cfb
    
8a8cfb
    Change-Id: Iad079080ebe7442c13313ba11dc2797953faef35
8a8cfb
8a8cfb
diff --git a/elf/dl-open.c b/elf/dl-open.c
8a8cfb
index 79c6e4c8ed1c9dfa..25838b073ac1edaf 100644
8a8cfb
--- a/elf/dl-open.c
8a8cfb
+++ b/elf/dl-open.c
8a8cfb
@@ -34,6 +34,7 @@
8a8cfb
 #include <atomic.h>
8a8cfb
 #include <libc-internal.h>
8a8cfb
 #include <array_length.h>
8a8cfb
+#include <internal-signals.h>
8a8cfb
 
8a8cfb
 #include <dl-dst.h>
8a8cfb
 #include <dl-prop.h>
8a8cfb
@@ -52,6 +53,10 @@ struct dl_open_args
8a8cfb
   /* Namespace ID.  */
8a8cfb
   Lmid_t nsid;
8a8cfb
 
8a8cfb
+  /* Original signal mask.  Used for unblocking signal handlers before
8a8cfb
+     running ELF constructors.  */
8a8cfb
+  sigset_t original_signal_mask;
8a8cfb
+
8a8cfb
   /* Original value of _ns_global_scope_pending_adds.  Set by
8a8cfb
      dl_open_worker.  Only valid if nsid is a real namespace
8a8cfb
      (non-negative).  */
8a8cfb
@@ -524,12 +529,16 @@ dl_open_worker (void *a)
8a8cfb
   if (new == NULL)
8a8cfb
     {
8a8cfb
       assert (mode & RTLD_NOLOAD);
8a8cfb
+      __libc_signal_restore_set (&args->original_signal_mask);
8a8cfb
       return;
8a8cfb
     }
8a8cfb
 
8a8cfb
   if (__glibc_unlikely (mode & __RTLD_SPROF))
8a8cfb
-    /* This happens only if we load a DSO for 'sprof'.  */
8a8cfb
-    return;
8a8cfb
+    {
8a8cfb
+      /* This happens only if we load a DSO for 'sprof'.  */
8a8cfb
+      __libc_signal_restore_set (&args->original_signal_mask);
8a8cfb
+      return;
8a8cfb
+    }
8a8cfb
 
8a8cfb
   /* This object is directly loaded.  */
8a8cfb
   ++new->l_direct_opencount;
8a8cfb
@@ -565,6 +574,7 @@ dl_open_worker (void *a)
8a8cfb
 
8a8cfb
       assert (_dl_debug_initialize (0, args->nsid)->r_state == RT_CONSISTENT);
8a8cfb
 
8a8cfb
+      __libc_signal_restore_set (&args->original_signal_mask);
8a8cfb
       return;
8a8cfb
     }
8a8cfb
 
8a8cfb
@@ -745,6 +755,10 @@ dl_open_worker (void *a)
8a8cfb
   if (mode & RTLD_GLOBAL)
8a8cfb
     add_to_global_resize (new);
8a8cfb
 
8a8cfb
+  /* Unblock signals.  Data structures are now consistent, and
8a8cfb
+     application code may run.  */
8a8cfb
+  __libc_signal_restore_set (&args->original_signal_mask);
8a8cfb
+
8a8cfb
   /* Run the initializer functions of new objects.  Temporarily
8a8cfb
      disable the exception handler, so that lazy binding failures are
8a8cfb
      fatal.  */
8a8cfb
@@ -834,6 +848,10 @@ no more namespaces available for dlmopen()"));
8a8cfb
   args.argv = argv;
8a8cfb
   args.env = env;
8a8cfb
 
8a8cfb
+  /* Recursive lazy binding during manipulation of the dynamic loader
8a8cfb
+     structures may result in incorrect behavior.  */
8a8cfb
+  __libc_signal_block_all (&args.original_signal_mask);
8a8cfb
+
8a8cfb
   struct dl_exception exception;
8a8cfb
   int errcode = _dl_catch_exception (&exception, dl_open_worker, &args);
8a8cfb
 
8a8cfb
@@ -874,10 +892,16 @@ no more namespaces available for dlmopen()"));
8a8cfb
 
8a8cfb
 	  _dl_close_worker (args.map, true);
8a8cfb
 
8a8cfb
+	  /* Restore the signal mask.  In the success case, this
8a8cfb
+	     happens inside dl_open_worker.  */
8a8cfb
+	  __libc_signal_restore_set (&args.original_signal_mask);
8a8cfb
+
8a8cfb
 	  /* All link_map_nodelete_pending objects should have been
8a8cfb
 	     deleted at this point, which is why it is not necessary
8a8cfb
 	     to reset the flag here.  */
8a8cfb
 	}
8a8cfb
+      else
8a8cfb
+	__libc_signal_restore_set (&args.original_signal_mask);
8a8cfb
 
8a8cfb
       assert (_dl_debug_initialize (0, args.nsid)->r_state == RT_CONSISTENT);
8a8cfb