From 0551c1d47bad202ce6dd90757067bc0fd217497a Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Mon, 11 Apr 2016 12:42:58 -0700 Subject: [PATCH] Remove dangling links in secondary namespaces linker didn't remove link to the soinfo from shared namespaces on soinfo_unload, because it didn't keep record of all namespaces the library is added to. This change adds test for this and also fixes the problem by introducing list of secondary namespaces to soinfo, which is used to remove soinfo in soinfo::remove_all_links(). Bug: http://b/28115950 Change-Id: Ifbf6e54f92fa6e88f86b6a8dd6dc22d4553afd22 (cherry picked from commit aca299ac4721809d6fc61e25c505bb59acd23fbc) --- linker/linker.cpp | 50 +++++++++++++++++++++++++++++++--------- linker/linker.h | 17 ++++++++++++-- tests/dlext_test.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 118 insertions(+), 13 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index a4dae3ef7..d6ee8cfb2 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -111,6 +111,7 @@ struct android_namespace_t { void add_soinfos(const soinfo::soinfo_list_t& soinfos) { for (auto si : soinfos) { add_soinfo(si); + si->add_secondary_namespace(this); } } @@ -147,6 +148,7 @@ static LinkerTypeAllocator g_soinfo_allocator; static LinkerTypeAllocator> g_soinfo_links_allocator; static LinkerTypeAllocator g_namespace_allocator; +static LinkerTypeAllocator> g_namespace_list_allocator; static soinfo* solist; static soinfo* sonext; @@ -352,6 +354,14 @@ void SoinfoListAllocator::free(LinkedListEntry* entry) { g_soinfo_links_allocator.free(entry); } +LinkedListEntry* NamespaceListAllocator::alloc() { + return g_namespace_list_allocator.alloc(); +} + +void NamespaceListAllocator::free(LinkedListEntry* entry) { + g_namespace_list_allocator.free(entry); +} + static soinfo* soinfo_alloc(android_namespace_t* ns, const char* name, struct stat* file_stat, off64_t file_offset, uint32_t rtld_flags) { @@ -415,9 +425,6 @@ static void soinfo_free(soinfo* si) { sonext = prev; } - // remove from the namespace - si->get_namespace()->remove_soinfo(si); - si->~soinfo(); g_soinfo_allocator.free(si); } @@ -909,7 +916,7 @@ soinfo::soinfo(android_namespace_t* ns, const char* realpath, } this->rtld_flags_ = rtld_flags; - this->namespace_ = ns; + this->primary_namespace_ = ns; } soinfo::~soinfo() { @@ -1069,6 +1076,7 @@ class ProtectedDataGuard { g_soinfo_allocator.protect_all(protection); g_soinfo_links_allocator.protect_all(protection); g_namespace_allocator.protect_all(protection); + g_namespace_list_allocator.protect_all(protection); } static size_t ref_count_; @@ -2224,7 +2232,7 @@ static void soinfo_unload(soinfo* root) { TRACE("deprecated (old format of soinfo): %s needs to unload %s", si->get_realpath(), library_name); - soinfo* needed = find_library(si->get_namespace(), + soinfo* needed = find_library(si->get_primary_namespace(), library_name, RTLD_NOLOAD, nullptr, nullptr); if (needed != nullptr) { @@ -2274,6 +2282,10 @@ static std::string symbol_display_name(const char* sym_name, const char* sym_ver return std::string(sym_name) + ", version " + sym_ver; } +static android_namespace_t* get_caller_namespace(soinfo* caller) { + return caller != nullptr ? caller->get_primary_namespace() : g_anonymous_namespace; +} + 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. @@ -2310,7 +2322,7 @@ void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, return nullptr; } - android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace; + android_namespace_t* ns = get_caller_namespace(caller); if (extinfo != nullptr) { if ((extinfo->flags & ~(ANDROID_DLEXT_VALID_FLAG_BITS)) != 0) { @@ -2404,7 +2416,7 @@ bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver, soinfo* found = nullptr; const ElfW(Sym)* sym = nullptr; soinfo* caller = find_containing_library(caller_addr); - android_namespace_t* ns = caller != nullptr ? caller->get_namespace() : g_anonymous_namespace; + android_namespace_t* ns = get_caller_namespace(caller); version_info vi_instance; version_info* vi = nullptr; @@ -2517,7 +2529,7 @@ android_namespace_t* create_namespace(const void* caller_addr, soinfo* caller_soinfo = find_containing_library(caller_addr); android_namespace_t* caller_ns = caller_soinfo != nullptr ? - caller_soinfo->get_namespace() : + caller_soinfo->get_primary_namespace() : g_anonymous_namespace; ProtectedDataGuard guard; @@ -3156,9 +3168,20 @@ void soinfo::remove_all_links() { }); }); - // 2. Once everything untied - clear local lists. + // 2. Remove from the primary namespace + primary_namespace_->remove_soinfo(this); + primary_namespace_ = nullptr; + + // 3. Remove from secondary namespaces + secondary_namespaces_.for_each([&](android_namespace_t* ns) { + ns->remove_soinfo(this); + }); + + + // 4. Once everything untied - clear local lists. parents_.clear(); children_.clear(); + secondary_namespaces_.clear(); } dev_t soinfo::get_st_dev() const { @@ -3292,14 +3315,19 @@ const std::vector& soinfo::get_dt_runpath() const { return g_empty_runpath; } -android_namespace_t* soinfo::get_namespace() { +android_namespace_t* soinfo::get_primary_namespace() { if (has_min_version(3)) { - return namespace_; + return primary_namespace_; } return &g_default_namespace; } +void soinfo::add_secondary_namespace(android_namespace_t* secondary_ns) { + CHECK(has_min_version(3)); + secondary_namespaces_.push_back(secondary_ns); +} + ElfW(Addr) soinfo::resolve_symbol_address(const ElfW(Sym)* s) const { if (ELF_ST_TYPE(s->st_info) == STT_GNU_IFUNC) { return call_ifunc_resolver(s->st_value + load_bias); diff --git a/linker/linker.h b/linker/linker.h index a27374ca0..a776525f8 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -114,6 +114,16 @@ class SoinfoListAllocator { DISALLOW_IMPLICIT_CONSTRUCTORS(SoinfoListAllocator); }; +class NamespaceListAllocator { + public: + static LinkedListEntry* alloc(); + static void free(LinkedListEntry* entry); + + private: + // unconstructable + DISALLOW_IMPLICIT_CONSTRUCTORS(NamespaceListAllocator); +}; + class SymbolName { public: explicit SymbolName(const char* name) @@ -166,6 +176,7 @@ class VersionTracker { struct soinfo { public: typedef LinkedList soinfo_list_t; + typedef LinkedList android_namespace_list_t; #if defined(__work_around_b_24465209__) private: char old_name_[SOINFO_NAME_LEN]; @@ -340,7 +351,8 @@ struct soinfo { void set_dt_runpath(const char *); const std::vector& get_dt_runpath() const; - android_namespace_t* get_namespace(); + android_namespace_t* get_primary_namespace(); + void add_secondary_namespace(android_namespace_t* secondary_ns); void set_mapped_by_caller(bool reserved_map); bool is_mapped_by_caller() const; @@ -412,7 +424,8 @@ struct soinfo { // version >= 3 std::vector dt_runpath_; - android_namespace_t* namespace_; + android_namespace_t* primary_namespace_; + android_namespace_list_t secondary_namespaces_; uintptr_t handle_; friend soinfo* get_libdl_info(); diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index bbdc024ec..87e5dbc09 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -959,6 +959,70 @@ TEST(dlext, ns_shared) { dlclose(handle2); } +TEST(dlext, ns_shared_dlclose) { + std::string path = "libc.so:libc++.so:libdl.so:libm.so"; + + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH; + + android_set_application_target_sdk_version(42U); // something > 23 + + ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror(); + + // preload this library to the default namespace to check if it + // is shared later on. + void* handle_dlopened = + dlopen((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(), RTLD_NOW); + ASSERT_TRUE(handle_dlopened != nullptr) << dlerror(); + + android_namespace_t* ns_isolated_shared = + android_create_namespace("private_isolated_shared", nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED, + nullptr); + ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror(); + + // Check if "libnstest_dlopened.so" is loaded (and the same) + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns_isolated_shared; + + void* handle = android_dlopen_ext("libnstest_dlopened.so", RTLD_NOW | RTLD_NOLOAD, &extinfo); + ASSERT_TRUE(handle != nullptr) << dlerror(); + ASSERT_TRUE(handle == handle_dlopened); + dlclose(handle); + dlclose(handle_dlopened); + + // And now check that the library cannot be found by soname (and is no longer loaded) + handle = android_dlopen_ext("libnstest_dlopened.so", RTLD_NOW | RTLD_NOLOAD, &extinfo); + ASSERT_TRUE(handle == nullptr) + << "Error: libnstest_dlopened.so is still accessible in shared namespace"; + + handle = android_dlopen_ext((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(), + RTLD_NOW | RTLD_NOLOAD, &extinfo); + ASSERT_TRUE(handle == nullptr) + << "Error: libnstest_dlopened.so is still accessible in shared namespace"; + + handle = dlopen("libnstest_dlopened.so", RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr) + << "Error: libnstest_dlopened.so is still accessible in default namespace"; + + handle = dlopen((lib_path + "/private_namespace_libs/libnstest_dlopened.so").c_str(), + RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle == nullptr) + << "Error: libnstest_dlopened.so is still accessible in default namespace"; + + // Now lets see if the soinfo area gets reused in the wrong way: + // load a library to default namespace. + const std::string lib_public_path = lib_path + "/public_namespace_libs/" + g_public_lib; + void* handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW); + ASSERT_TRUE(handle_public != nullptr) << dlerror(); + + // try to find it in shared namespace + handle = android_dlopen_ext(g_public_lib, RTLD_NOW | RTLD_NOLOAD, &extinfo); + ASSERT_TRUE(handle == nullptr) + << "Error: " << g_public_lib << " is accessible in shared namespace"; +} + TEST(dlext, ns_anonymous) { static const char* root_lib = "libnstest_root.so"; std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib; -- 2.11.0