[RFC,5/5] elf/dl-fini.c: Handle cloned link_map entries in the shutdown path

Message ID 20180424162654.17761-6-vivek@collabora.com
State New, archived
Headers

Commit Message

Vivek Dasmohapatra April 24, 2018, 4:26 p.m. UTC
  When cleaning up before exit we should not call destructors or
otherwise free [most of] the contents of a cloned link_map entry
since they share [most of] their contents with the LM_ID_BASE
object from which they were cloned.

Instead we do the minimal cleanup necessary and remove them from
the namespace linked list(s) before the main cleanup code path
is triggered: This prevemts double-frees and double-invocation
of any destructors (which might not be idempotent).
---
 elf/dl-fini.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 51 insertions(+)
  

Patch

diff --git a/elf/dl-fini.c b/elf/dl-fini.c
index 3cfc262400..f6cab8b1cc 100644
--- a/elf/dl-fini.c
+++ b/elf/dl-fini.c
@@ -24,6 +24,51 @@ 
 /* Type of the constructor functions.  */
 typedef void (*fini_t) (void);
 
+/* Remove (and free) cloned entries from the namespace specifid by `ns'.  */
+void
+internal_function
+_dl_forget_clones (Lmid_t ns)
+{
+#ifdef SHARED /* Obviously none of this applies if */
+  struct link_map *clone;
+  struct link_map *next;
+
+  if (ns < 0 || ns >= DL_NNS)
+    return;
+
+  for (clone = GL(dl_ns)[ns]._ns_loaded; clone != NULL; clone = next)
+    {
+      next = clone->l_next;
+
+      /* Not actually clone, don't care.  */
+      if (!clone->l_clone)
+        continue;
+
+      if (__glibc_unlikely (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS))
+        _dl_debug_printf ("\nclone removed %s [%lu]\n", clone->l_name, ns);
+
+      /* If item is a clone, slice it out of the linked list.  */
+      if (clone == GL(dl_ns)[ns]._ns_loaded)
+        GL(dl_ns)[ns]._ns_loaded = clone->l_next;
+      else
+        clone->l_prev->l_next = clone->l_next;
+
+      /* Remember to fix up the links in both directions.  */
+      if (clone->l_next)
+        clone->l_next->l_prev = clone->l_prev;
+
+      /* Don't need to do most destructor handling for clones.  */
+      if (clone->l_free_initfini)
+        free (clone->l_initfini);
+
+      /* Do need to fix the global load count which is updated in
+         _dl_add_to_namespace_list.  */
+      GL(dl_ns)[ns]._ns_nloaded--;
+
+      free (clone);
+    }
+#endif
+}
 
 void
 _dl_fini (void)
@@ -52,6 +97,12 @@  _dl_fini (void)
       /* Protect against concurrent loads and unloads.  */
       __rtld_lock_lock_recursive (GL(dl_load_lock));
 
+      /* We need to remove any pointers to cloned entries (link_map
+         structs that are copied from one namespace to another to
+         implement RTLD_SHARED semantics) before the regular cleanup
+         code gets to them.  */
+      _dl_forget_clones (ns);
+
       unsigned int nloaded = GL(dl_ns)[ns]._ns_nloaded;
       /* No need to do anything for empty namespaces or those used for
 	 auditing DSOs.  */