diff --git a/linker/linker.cpp b/linker/linker.cpp index f3370ec2b..77f5359f3 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -110,6 +110,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); } } @@ -146,6 +147,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; @@ -286,6 +288,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) { @@ -349,9 +359,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); } @@ -843,7 +850,7 @@ soinfo::soinfo(android_namespace_t* ns, const char* realpath, } this->rtld_flags_ = rtld_flags; - this->namespace_ = ns; + this->primary_namespace_ = ns; } soinfo::~soinfo() { @@ -1003,6 +1010,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_; @@ -2122,7 +2130,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) { @@ -2172,6 +2180,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. @@ -2208,7 +2220,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) { @@ -2302,7 +2314,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; @@ -2415,7 +2427,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; @@ -3050,9 +3062,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 { @@ -3186,14 +3209,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 81f93ac2f..4e2e0b90c 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]; @@ -342,7 +353,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; @@ -414,7 +426,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;