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