mirror of
https://sourceware.org/git/glibc.git
synced 2025-07-29 11:41:21 +03:00
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 f63b73814f
("Remove all
loaded objects if dlopen fails, ignoring NODELETE [BZ #20839]").
This commit is contained in:
@ -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;
|
||||
}
|
||||
/* 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);
|
||||
|
||||
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);
|
||||
|
||||
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
|
||||
|
Reference in New Issue
Block a user