diff options
author | Carlos O'Donell <carlos@redhat.com> | 2016-12-23 13:30:22 -0500 |
---|---|---|
committer | Carlos O'Donell <carlos@redhat.com> | 2016-12-23 13:30:22 -0500 |
commit | 57707b7fcc38855869321f8c7827bfe21d729f37 (patch) | |
tree | 35c04e405d4e374096095f88af4c2408d90392e6 /elf/dl-close.c | |
parent | b064bba552e38e08a69a91424247ae67de493345 (diff) | |
download | glibc-57707b7fcc38855869321f8c7827bfe21d729f37.tar.gz glibc-57707b7fcc38855869321f8c7827bfe21d729f37.tar.xz glibc-57707b7fcc38855869321f8c7827bfe21d729f37.zip |
Bug 11941: ld.so: Improper assert map->l_init_called in dlclose
There is at least one use case where during exit a library destructor might call dlclose() on a valid handle and have it fail with an assertion. We must allow this case, it is a valid handle, and dlclose() should not fail with an assert. In the future we might be able to return an error that the dlclose() could not be completed because the opened library has already been unloaded and destructors have run as part of exit processing. For more details see: https://www.sourceware.org/ml/libc-alpha/2016-12/msg00859.html
Diffstat (limited to 'elf/dl-close.c')
-rw-r--r-- | elf/dl-close.c | 30 |
1 files changed, 24 insertions, 6 deletions
diff --git a/elf/dl-close.c b/elf/dl-close.c index 648970332e..9f93ab7628 100644 --- a/elf/dl-close.c +++ b/elf/dl-close.c @@ -805,19 +805,37 @@ _dl_close (void *_map) { struct link_map *map = _map; - /* First see whether we can remove the object at all. */ + /* We must take the lock to examine the contents of map and avoid + concurrent dlopens. */ + __rtld_lock_lock_recursive (GL(dl_load_lock)); + + /* At this point we are guaranteed nobody else is touching the list of + loaded maps, but a concurrent dlclose might have freed our map + before we took the lock. There is no way to detect this (see below) + so we proceed assuming this isn't the case. First see whether we + can remove the object at all. */ if (__glibc_unlikely (map->l_flags_1 & DF_1_NODELETE)) { - assert (map->l_init_called); /* Nope. Do nothing. */ + __rtld_lock_unlock_recursive (GL(dl_load_lock)); return; } + /* At present this is an unreliable check except in the case where the + caller has recursively called dlclose and we are sure the link map + has not been freed. In a non-recursive dlclose the map itself + might have been freed and this access is potentially a data race + with whatever other use this memory might have now, or worse we + might silently corrupt memory if it looks enough like a link map. + POSIX has language in dlclose that appears to guarantee that this + should be a detectable case and given that dlclose should be threadsafe + we need this to be a reliable detection. + This is bug 20990. */ if (__builtin_expect (map->l_direct_opencount, 1) == 0) - _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); - - /* Acquire the lock. */ - __rtld_lock_lock_recursive (GL(dl_load_lock)); + { + __rtld_lock_unlock_recursive (GL(dl_load_lock)); + _dl_signal_error (0, map->l_name, NULL, N_("shared object not open")); + } _dl_close_worker (map, false); |