diff options
author | Florian Weimer <fweimer@redhat.com> | 2019-12-13 10:18:46 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2019-12-13 10:18:46 +0100 |
commit | f8ed116aa574435c6e28260f21963233682d3b57 (patch) | |
tree | 859f2077eec2bc1e19f99e4853c4a8bf1c2f7848 /elf/dl-lookup.c | |
parent | 365624e2d2a342cdb693b4cc35d2312169959e28 (diff) | |
download | glibc-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 'elf/dl-lookup.c')
-rw-r--r-- | elf/dl-lookup.c | 58 |
1 files changed, 34 insertions, 24 deletions
diff --git a/elf/dl-lookup.c b/elf/dl-lookup.c index 99846918c3..759b45a2c9 100644 --- a/elf/dl-lookup.c +++ b/elf/dl-lookup.c @@ -187,6 +187,28 @@ enter_unique_sym (struct unique_sym *table, size_t size, table[idx].map = map; } +/* Mark MAP as NODELETE according to the lookup mode in FLAGS. During + initial relocation, NODELETE state is pending only. */ +static void +mark_nodelete (struct link_map *map, int flags) +{ + if (flags & DL_LOOKUP_FOR_RELOCATE) + map->l_nodelete_pending = true; + else + map->l_nodelete_active = true; +} + +/* Return true if MAP is marked as NODELETE according to the lookup + mode in FLAGS> */ +static bool +is_nodelete (struct link_map *map, int flags) +{ + /* Non-pending NODELETE always counts. Pending NODELETE only counts + during initial relocation processing. */ + return map->l_nodelete_active + || ((flags & DL_LOOKUP_FOR_RELOCATE) && map->l_nodelete_pending); +} + /* Utility function for do_lookup_x. Lookup an STB_GNU_UNIQUE symbol in the unique symbol table, creating a new entry if necessary. Return the matching symbol in RESULT. */ @@ -311,8 +333,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, enter_unique_sym (entries, size, new_hash, strtab + sym->st_name, sym, map); - if (map->l_type == lt_loaded - && map->l_nodelete == link_map_nodelete_inactive) + if (map->l_type == lt_loaded && !is_nodelete (map, flags)) { /* Make sure we don't unload this object by setting the appropriate flag. */ @@ -320,10 +341,7 @@ do_lookup_unique (const char *undef_name, uint_fast32_t new_hash, _dl_debug_printf ("\ marking %s [%lu] as NODELETE due to unique symbol\n", map->l_name, map->l_ns); - if (flags & DL_LOOKUP_FOR_RELOCATE) - map->l_nodelete = link_map_nodelete_pending; - else - map->l_nodelete = link_map_nodelete_active; + mark_nodelete (map, flags); } } ++tab->n_elements; @@ -586,7 +604,7 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags) dependencies may pick an dependency which can be dlclose'd, but such IFUNC resolvers are undefined anyway. */ assert (map->l_type == lt_loaded); - if (map->l_nodelete != link_map_nodelete_inactive) + if (is_nodelete (map, flags)) return 0; struct link_map_reldeps *l_reldeps @@ -694,17 +712,16 @@ add_dependency (struct link_map *undef_map, struct link_map *map, int flags) /* 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) + if (is_nodelete (map, flags)) goto out; /* If the object with the undefined reference cannot be removed ever just make sure the same is true for the object which contains the definition. */ - if (undef_map->l_type != lt_loaded - || (undef_map->l_nodelete != link_map_nodelete_inactive)) + if (undef_map->l_type != lt_loaded || is_nodelete (map, flags)) { if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) - && map->l_nodelete == link_map_nodelete_inactive) + && !is_nodelete (map, flags)) { if (undef_map->l_name[0] == '\0') _dl_debug_printf ("\ @@ -716,11 +733,7 @@ marking %s [%lu] as NODELETE due to reference to %s [%lu]\n", map->l_name, map->l_ns, undef_map->l_name, undef_map->l_ns); } - - if (flags & DL_LOOKUP_FOR_RELOCATE) - map->l_nodelete = link_map_nodelete_pending; - else - map->l_nodelete = link_map_nodelete_active; + mark_nodelete (map, flags); goto out; } @@ -746,17 +759,14 @@ marking %s [%lu] as NODELETE due to reference to %s [%lu]\n", cannot be unloaded. This is semantically the correct behavior. */ if (__glibc_unlikely (GLRO (dl_debug_mask) & DL_DEBUG_BINDINGS) - && map->l_nodelete == link_map_nodelete_inactive) + && !is_nodelete (map, flags)) _dl_debug_printf ("\ marking %s [%lu] as NODELETE due to memory allocation failure\n", map->l_name, map->l_ns); - if (flags & DL_LOOKUP_FOR_RELOCATE) - /* In case of non-lazy binding, we could actually - report the memory allocation error, but for now, we - use the conservative approximation as well. */ - map->l_nodelete = link_map_nodelete_pending; - else - map->l_nodelete = link_map_nodelete_active; + /* In case of non-lazy binding, we could actually report + the memory allocation error, but for now, we use the + conservative approximation as well. */ + mark_nodelete (map, flags); goto out; } else |