From a2547055f25db614601ee8651f2e42ece01f7842 Mon Sep 17 00:00:00 2001 From: Dmitriy Ivanov Date: Tue, 18 Nov 2014 12:03:09 -0800 Subject: [PATCH] Fix jump to unmapped memory on atexit Split d-tor calls and soinfo_free to 2 separate steps Bug: 18338888 Change-Id: Idbcb7242ade16fa18cba7fe30505ebd8d6023622 --- linker/linker.cpp | 26 ++++++++++++++----- ...dlopen_check_order_reloc_nephew_answer.cpp | 16 ++++++++++++ 2 files changed, 35 insertions(+), 7 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index 11d7b94a4..1d68db5a3 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1135,28 +1135,27 @@ static soinfo* find_library(const char* name, int rtld_flags, const android_dlex return si; } -static void soinfo_unload(soinfo* si) { +static void soinfo_unload_schedule(soinfo::soinfo_list_t& unload_list, soinfo* si) { if (!si->can_unload()) { TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->name); return; } if (si->ref_count == 1) { - TRACE("unloading '%s'", si->name); - si->call_destructors(); + unload_list.push_back(si); if (si->has_min_version(0)) { soinfo* child = nullptr; while ((child = si->get_children().pop_front()) != nullptr) { TRACE("%s needs to unload %s", si->name, child->name); - soinfo_unload(child); + soinfo_unload_schedule(unload_list, child); } } else { for_each_dt_needed(si, [&] (const char* library_name) { TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, library_name); soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr); if (needed != nullptr) { - soinfo_unload(needed); + soinfo_unload_schedule(unload_list, needed); } else { // Not found: for example if symlink was deleted between dlopen and dlclose // Since we cannot really handle errors at this point - print and continue. @@ -1165,15 +1164,28 @@ static void soinfo_unload(soinfo* si) { }); } - notify_gdb_of_unload(si); si->ref_count = 0; - soinfo_free(si); } else { si->ref_count--; TRACE("not unloading '%s', decrementing ref_count to %zd", si->name, si->ref_count); } } +static void soinfo_unload(soinfo* root) { + soinfo::soinfo_list_t unload_list; + soinfo_unload_schedule(unload_list, root); + unload_list.for_each([](soinfo* si) { + si->call_destructors(); + }); + + soinfo* si = nullptr; + while ((si = unload_list.pop_front()) != nullptr) { + TRACE("unloading '%s'", si->name); + notify_gdb_of_unload(si); + soinfo_free(si); + } +} + void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) { // Use basic string manipulation calls to avoid snprintf. // snprintf indirectly calls pthread_getspecific to get the size of a buffer. diff --git a/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp b/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp index 065d1bef7..d6d1f092b 100644 --- a/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp +++ b/tests/libs/dlopen_check_order_reloc_nephew_answer.cpp @@ -19,3 +19,19 @@ extern "C" int check_order_reloc_get_answer_impl(); extern "C" int check_order_reloc_nephew_get_answer() { return check_order_reloc_get_answer_impl(); } + +namespace { +// The d-tor for this class is called on dlclose() -> __on_dlclose() -> __cxa_finalize() +// We use it to detect calls to prematurely unmapped libraries during dlclose. +// See also b/18338888 +class CallNephewInDtor { + public: + ~CallNephewInDtor() { + check_order_reloc_get_answer_impl(); + } +} instance; +}; + +extern "C" void* get_instance() { + return &instance; +}