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