Fix jump to unmapped memory on atexit

Split d-tor calls and soinfo_free to 2 separate steps

Bug: 18338888
Change-Id: Idbcb7242ade16fa18cba7fe30505ebd8d6023622
This commit is contained in:
Dmitriy Ivanov 2014-11-18 12:03:09 -08:00
parent 8eda0a6d69
commit a2547055f2
2 changed files with 35 additions and 7 deletions

View File

@ -1135,28 +1135,27 @@ static soinfo* find_library(const char* name, int rtld_flags, const android_dlex
return si; 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()) { if (!si->can_unload()) {
TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->name); TRACE("not unloading '%s' - the binary is flagged with NODELETE", si->name);
return; return;
} }
if (si->ref_count == 1) { if (si->ref_count == 1) {
TRACE("unloading '%s'", si->name); unload_list.push_back(si);
si->call_destructors();
if (si->has_min_version(0)) { if (si->has_min_version(0)) {
soinfo* child = nullptr; soinfo* child = nullptr;
while ((child = si->get_children().pop_front()) != nullptr) { while ((child = si->get_children().pop_front()) != nullptr) {
TRACE("%s needs to unload %s", si->name, child->name); TRACE("%s needs to unload %s", si->name, child->name);
soinfo_unload(child); soinfo_unload_schedule(unload_list, child);
} }
} else { } else {
for_each_dt_needed(si, [&] (const char* library_name) { for_each_dt_needed(si, [&] (const char* library_name) {
TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->name, 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); soinfo* needed = find_library(library_name, RTLD_NOLOAD, nullptr);
if (needed != nullptr) { if (needed != nullptr) {
soinfo_unload(needed); soinfo_unload_schedule(unload_list, needed);
} else { } else {
// Not found: for example if symlink was deleted between dlopen and dlclose // Not found: for example if symlink was deleted between dlopen and dlclose
// Since we cannot really handle errors at this point - print and continue. // 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; si->ref_count = 0;
soinfo_free(si);
} else { } else {
si->ref_count--; si->ref_count--;
TRACE("not unloading '%s', decrementing ref_count to %zd", si->name, 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) { void do_android_get_LD_LIBRARY_PATH(char* buffer, size_t buffer_size) {
// Use basic string manipulation calls to avoid snprintf. // Use basic string manipulation calls to avoid snprintf.
// snprintf indirectly calls pthread_getspecific to get the size of a buffer. // snprintf indirectly calls pthread_getspecific to get the size of a buffer.

View File

@ -19,3 +19,19 @@ extern "C" int check_order_reloc_get_answer_impl();
extern "C" int check_order_reloc_nephew_get_answer() { extern "C" int check_order_reloc_nephew_get_answer() {
return check_order_reloc_get_answer_impl(); 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;
}