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