diff options
author | Florian Weimer <fweimer@redhat.com> | 2019-12-13 10:18:46 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2019-12-13 10:18:46 +0100 |
commit | f8ed116aa574435c6e28260f21963233682d3b57 (patch) | |
tree | 859f2077eec2bc1e19f99e4853c4a8bf1c2f7848 /elf/dl-close.c | |
parent | 365624e2d2a342cdb693b4cc35d2312169959e28 (diff) | |
download | glibc-f8ed116aa574435c6e28260f21963233682d3b57.tar.gz glibc-f8ed116aa574435c6e28260f21963233682d3b57.tar.xz 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.c | 7 |
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)); |