mirror of
git://sourceware.org/git/glibc.git
synced 2025-01-30 12:31:53 +08:00
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
This commit is contained in:
parent
f63b73814f
commit
a2e8aa0d9e
@ -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;
|
||||
}
|
||||
|
||||
@ -748,6 +758,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. */
|
||||
@ -837,6 +851,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);
|
||||
|
||||
@ -877,10 +895,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);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user