aboutsummaryrefslogtreecommitdiff
path: root/elf/dl-open.c
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-12-13 10:18:24 +0100
committerFlorian Weimer <fweimer@redhat.com>2019-12-13 10:18:24 +0100
commit365624e2d2a342cdb693b4cc35d2312169959e28 (patch)
tree4a17435022fd7b0c03690c7ad3444b0d3c030ced /elf/dl-open.c
parent186e119bbd4a10895429ffe405ae96dc5c5634b8 (diff)
downloadglibc-365624e2d2a342cdb693b4cc35d2312169959e28.zip
glibc-365624e2d2a342cdb693b4cc35d2312169959e28.tar.gz
glibc-365624e2d2a342cdb693b4cc35d2312169959e28.tar.bz2
dlopen: Fix issues related to NODELETE handling and relocations
The assumption behind the assert in activate_nodelete was wrong: Inconsistency detected by ld.so: dl-open.c: 459: activate_nodelete: Assertion `!imap->l_init_called || imap->l_type != lt_loaded' failed! (edit) It can happen that an already-loaded object that is in the local scope is promoted to NODELETE status, via binding to a unique symbol. Similarly, it is possible that such NODELETE promotion occurs to an already-loaded object from the global scope. This is why the loop in activate_nodelete has to cover all objects in the namespace of the new object. In do_lookup_unique, it could happen that the NODELETE status of an already-loaded object was overwritten with a pending NODELETE status. As a result, if dlopen fails, this could cause a loss of the NODELETE status of the affected object, eventually resulting in an incorrect unload. Fixes commit f63b73814f74032c0e5d0a83300e3d864ef905e5 ("Remove all loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]").
Diffstat (limited to 'elf/dl-open.c')
-rw-r--r--elf/dl-open.c41
1 files changed, 14 insertions, 27 deletions
diff --git a/elf/dl-open.c b/elf/dl-open.c
index df9f29a..56f2133 100644
--- a/elf/dl-open.c
+++ b/elf/dl-open.c
@@ -433,34 +433,21 @@ TLS generation counter wrapped! Please report this."));
after dlopen failure is not possible, so that _dl_close can clean
up objects if necessary. */
static void
-activate_nodelete (struct link_map *new, int mode)
+activate_nodelete (struct link_map *new)
{
- if (mode & RTLD_NODELETE || new->l_nodelete == link_map_nodelete_pending)
- {
- if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
- _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
- new->l_name, new->l_ns);
- new->l_nodelete = link_map_nodelete_active;
- }
-
- for (unsigned int i = 0; i < new->l_searchlist.r_nlist; ++i)
- {
- struct link_map *imap = new->l_searchlist.r_list[i];
- if (imap->l_nodelete == link_map_nodelete_pending)
- {
- if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
- _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
- imap->l_name, imap->l_ns);
-
- /* Only new objects should have set
- link_map_nodelete_pending. Existing objects should not
- have gained any new dependencies and therefore cannot
- reach NODELETE status. */
- assert (!imap->l_init_called || imap->l_type != lt_loaded);
+ /* It is necessary to traverse the entire namespace. References to
+ objects in the global scope and unique symbol bindings can force
+ NODELETE status for objects outside the local scope. */
+ for (struct link_map *l = GL (dl_ns)[new->l_ns]._ns_loaded; l != NULL;
+ l = l->l_next)
+ if (l->l_nodelete == link_map_nodelete_pending)
+ {
+ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_FILES))
+ _dl_debug_printf ("activating NODELETE for %s [%lu]\n",
+ l->l_name, l->l_ns);
- imap->l_nodelete = link_map_nodelete_active;
- }
- }
+ l->l_nodelete = link_map_nodelete_active;
+ }
}
/* struct dl_init_args and call_dl_init are used to call _dl_init with
@@ -721,7 +708,7 @@ dl_open_worker (void *a)
All memory allocations for new objects must have happened
before. */
- activate_nodelete (new, mode);
+ activate_nodelete (new);
/* Second stage after resize_scopes: Actually perform the scope
update. After this, dlsym and lazy binding can bind to new