aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-close.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-12-13 10:18:46 +0100
committerFlorian Weimer <fweimer@redhat.com>2019-12-13 10:18:46 +0100
commitf8ed116aa574435c6e28260f21963233682d3b57 (patch)
tree859f2077eec2bc1e19f99e4853c4a8bf1c2f7848 /elf/dl-close.c
parent365624e2d2a342cdb693b4cc35d2312169959e28 (diff)
downloadglibc-f8ed116aa574435c6e28260f21963233682d3b57.tar
glibc-f8ed116aa574435c6e28260f21963233682d3b57.tar.gz
glibc-f8ed116aa574435c6e28260f21963233682d3b57.tar.bz2
glibc-f8ed116aa574435c6e28260f21963233682d3b57.zip
dlopen: Rework handling of pending NODELETE status
Commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23 ("Block signals during the initial part of dlopen") was deemed necessary because of read-modify-write operations like the one in add_dependency in elf/dl-lookup.c. In the old code, we check for any kind of NODELETE status and bail out: /* Redo the NODELETE check, as when dl_load_lock wasn't held yet this could have changed. */ if (map->l_nodelete != link_map_nodelete_inactive) goto out; And then set pending status (during relocation): if (flags & DL_LOOKUP_FOR_RELOCATE) map->l_nodelete = link_map_nodelete_pending; else map->l_nodelete = link_map_nodelete_active; If a signal arrives during relocation and the signal handler, through lazy binding, adds a global scope dependency on the same map, it will set map->l_nodelete to link_map_nodelete_active. This will be overwritten with link_map_nodelete_pending by the dlopen relocation code. To avoid such problems in relation to the l_nodelete member, this commit introduces two flags for active NODELETE status (irrevocable) and pending NODELETE status (revocable until activate_nodelete is invoked). As a result, NODELETE processing in dlopen does not introduce further reasons why lazy binding from signal handlers is unsafe during dlopen, and a subsequent commit can remove signal blocking from dlopen. This does not address pre-existing issues (unrelated to the NODELETE changes) which make lazy binding in a signal handler during dlopen unsafe, such as the use of malloc in both cases. Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org> Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'elf/dl-close.c')
-rw-r--r--elf/dl-close.c7
1 files changed, 3 insertions, 4 deletions
diff --git a/elf/dl-close.c b/elf/dl-close.c
index e35a62daf6..df1df6fb29 100644
--- a/elf/dl-close.c
+++ b/elf/dl-close.c
@@ -197,7 +197,7 @@ _dl_close_worker (struct link_map *map, bool force)
/* Check whether this object is still used. */
if (l->l_type == lt_loaded
&& l->l_direct_opencount == 0
- && l->l_nodelete != link_map_nodelete_active
+ && !l->l_nodelete_active
/* See CONCURRENCY NOTES in cxa_thread_atexit_impl.c to know why
acquire is sufficient and correct. */
&& atomic_load_acquire (&l->l_tls_dtor_count) == 0
@@ -279,8 +279,7 @@ _dl_close_worker (struct link_map *map, bool force)
if (!used[i])
{
- assert (imap->l_type == lt_loaded
- && imap->l_nodelete != link_map_nodelete_active);
+ assert (imap->l_type == lt_loaded && !imap->l_nodelete_active);
/* Call its termination function. Do not do it for
half-cooked objects. Temporarily disable exception
@@ -830,7 +829,7 @@ _dl_close (void *_map)
before we took the lock. There is no way to detect this (see below)
so we proceed assuming this isn't the case. First see whether we
can remove the object at all. */
- if (__glibc_unlikely (map->l_nodelete == link_map_nodelete_active))
+ if (__glibc_unlikely (map->l_nodelete_active))
{
/* Nope. Do nothing. */
__rtld_lock_unlock_recursive (GL(dl_load_lock));