summary refs log tree commit diff
path: root/include/link.h
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2019-12-13 10:18:46 +0100
committerFlorian Weimer <fweimer@redhat.com>2019-12-13 10:18:46 +0100
commitf8ed116aa574435c6e28260f21963233682d3b57 (patch)
tree859f2077eec2bc1e19f99e4853c4a8bf1c2f7848 /include/link.h
parent365624e2d2a342cdb693b4cc35d2312169959e28 (diff)
downloadglibc-f8ed116aa574435c6e28260f21963233682d3b57.tar.gz
glibc-f8ed116aa574435c6e28260f21963233682d3b57.tar.xz
glibc-f8ed116aa574435c6e28260f21963233682d3b57.zip
dlopen: Rework handling of pending NODELETE status
Commit a2e8aa0d9ea648068d8be52dd7b15f1b6a008e23 ("Block signals during
the initial part of dlopen") was deemed necessary because of
read-modify-write operations like the one in  add_dependency in
elf/dl-lookup.c.  In the old code, we check for any kind of NODELETE
status and bail out:

      /* Redo the NODELETE check, as when dl_load_lock wasn't held
	 yet this could have changed.  */
      if (map->l_nodelete != link_map_nodelete_inactive)
	goto out;

And then set pending status (during relocation):

	  if (flags & DL_LOOKUP_FOR_RELOCATE)
	    map->l_nodelete = link_map_nodelete_pending;
	  else
	    map->l_nodelete = link_map_nodelete_active;

If a signal arrives during relocation and the signal handler, through
lazy binding, adds a global scope dependency on the same map, it will
set map->l_nodelete to link_map_nodelete_active.  This will be
overwritten with link_map_nodelete_pending by the dlopen relocation
code.

To avoid such problems in relation to the l_nodelete member, this
commit introduces two flags for active NODELETE status (irrevocable)
and pending NODELETE status (revocable until activate_nodelete is
invoked).  As a result, NODELETE processing in dlopen does not
introduce further reasons why lazy binding from signal handlers
is unsafe during dlopen, and a subsequent commit can remove signal
blocking from dlopen.

This does not address pre-existing issues (unrelated to the NODELETE
changes) which make lazy binding in a signal handler during dlopen
unsafe, such as the use of malloc in both cases.

Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Reviewed-by: Carlos O'Donell <carlos@redhat.com>
Diffstat (limited to 'include/link.h')
-rw-r--r--include/link.h31
1 files changed, 11 insertions, 20 deletions
diff --git a/include/link.h b/include/link.h
index 2e771e433a..2acc836380 100644
--- a/include/link.h
+++ b/include/link.h
@@ -79,22 +79,6 @@ struct r_search_path_struct
     int malloced;
   };
 
-/* Type used by the l_nodelete member.  */
-enum link_map_nodelete
-{
- /* This link map can be deallocated.  */
- link_map_nodelete_inactive = 0, /* Zero-initialized in _dl_new_object.  */
-
- /* This link map cannot be deallocated.  */
- link_map_nodelete_active,
-
- /* This link map cannot be deallocated after dlopen has succeded.
-    dlopen turns this into link_map_nodelete_active.  dlclose treats
-    this intermediate state as link_map_nodelete_active.  */
- link_map_nodelete_pending,
-};
-
-
 /* Structure describing a loaded shared object.  The `l_next' and `l_prev'
    members form a chain of all the shared objects loaded at startup.
 
@@ -218,10 +202,17 @@ struct link_map
 				       freed, ie. not allocated with
 				       the dummy malloc in ld.so.  */
 
-    /* Actually of type enum link_map_nodelete.  Separate byte due to
-       a read in add_dependency in elf/dl-lookup.c outside the loader
-       lock.  Only valid for l_type == lt_loaded.  */
-    unsigned char l_nodelete;
+    /* NODELETE status of the map.  Only valid for maps of type
+       lt_loaded.  Lazy binding sets l_nodelete_active directly,
+       potentially from signal handlers.  Initial loading of an
+       DF_1_NODELETE object set l_nodelete_pending.  Relocation may
+       set l_nodelete_pending as well.  l_nodelete_pending maps are
+       promoted to l_nodelete_active status in the final stages of
+       dlopen, prior to calling ELF constructors.  dlclose only
+       refuses to unload l_nodelete_active maps, the pending status is
+       ignored.  */
+    bool l_nodelete_active;
+    bool l_nodelete_pending;
 
 #include <link_map.h>