about summary refs log tree commit diff
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.tar.gz
glibc-365624e2d2a342cdb693b4cc35d2312169959e28.tar.xz
glibc-365624e2d2a342cdb693b4cc35d2312169959e28.zip
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 df9f29a5e5..56f213323c 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