1
0
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:
Florian Weimer
2019-12-13 10:18:24 +01:00
parent 186e119bbd
commit 365624e2d2
23 changed files with 992 additions and 32 deletions

View File

@ -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