linker: hide the pointer to soinfo Handle no longer is a pointer to soinfo of a corresponding library. This is done to prevent access to linker internal fields. Bug: http://b/25593965 Change-Id: I62bff0d0e5b2dc842e6bf0babb30fcc4c000be24 (cherry picked from commit d88e1f350111b3dfd71c6492321f0503cb5540db)
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index 64bf9e0..ce9fbe6 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp
@@ -69,7 +69,7 @@ static void* dlopen_ext(const char* filename, int flags, const android_dlextinfo* extinfo, void* caller_addr) { ScopedPthreadMutexLocker locker(&g_dl_mutex); - soinfo* result = do_dlopen(filename, flags, extinfo, caller_addr); + void* result = do_dlopen(filename, flags, extinfo, caller_addr); if (result == nullptr) { __bionic_format_dlerror("dlopen failed", linker_get_error_buffer()); return nullptr; @@ -87,8 +87,6 @@ return dlopen_ext(filename, flags, nullptr, caller_addr); } -extern android_namespace_t* g_anonymous_namespace; - void* dlsym_impl(void* handle, const char* symbol, const char* version, void* caller_addr) { ScopedPthreadMutexLocker locker(&g_dl_mutex); void* result; @@ -117,9 +115,11 @@ int dlclose(void* handle) { ScopedPthreadMutexLocker locker(&g_dl_mutex); - do_dlclose(reinterpret_cast<soinfo*>(handle)); - // dlclose has no defined errors. - return 0; + int result = do_dlclose(handle); + if (result != 0) { + __bionic_format_dlerror("dlclose failed", linker_get_error_buffer()); + } + return result; } int dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data) { @@ -270,6 +270,7 @@ __libdl_info->local_group_root_ = __libdl_info; __libdl_info->soname_ = "libdl.so"; __libdl_info->target_sdk_version_ = __ANDROID_API__; + __libdl_info->generate_handle(); #if defined(__work_around_b_24465209__) strlcpy(__libdl_info->old_name_, __libdl_info->soname_, sizeof(__libdl_info->old_name_)); #endif
diff --git a/linker/linked_list.h b/linker/linked_list.h index 88386b0..092e831 100644 --- a/linker/linked_list.h +++ b/linker/linked_list.h
@@ -43,7 +43,7 @@ return *this; } - T* operator*() { + T* const operator*() { return entry_->element; } @@ -190,15 +190,15 @@ return nullptr; } - iterator begin() { + iterator begin() const { return iterator(head_); } - iterator end() { + iterator end() const { return iterator(nullptr); } - iterator find(T* value) { + iterator find(T* value) const { for (LinkedListEntry<T>* e = head_; e != nullptr; e = e->next) { if (e->element == value) { return iterator(e);
diff --git a/linker/linker.cpp b/linker/linker.cpp index faf5759..1ad000c 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp
@@ -101,7 +101,23 @@ permitted_paths_ = permitted_paths; } - soinfo::soinfo_list_t& soinfo_list() { return soinfo_list_; } + void add_soinfo(soinfo* si) { + soinfo_list_.push_back(si); + } + + void add_soinfos(const soinfo::soinfo_list_t& soinfos) { + for (auto si : soinfos) { + add_soinfo(si); + } + } + + void remove_soinfo(soinfo* si) { + soinfo_list_.remove_if([&](soinfo* candidate) { + return si == candidate; + }); + } + + const soinfo::soinfo_list_t& soinfo_list() const { return soinfo_list_; } // For isolated namespaces - checks if the file is on the search path; // always returns true for not isolated namespace. @@ -119,7 +135,8 @@ }; android_namespace_t g_default_namespace; -android_namespace_t* g_anonymous_namespace = &g_default_namespace; +static std::unordered_map<uintptr_t, soinfo*> g_soinfo_handles_map; +static android_namespace_t* g_anonymous_namespace = &g_default_namespace; static ElfW(Addr) get_elf_exec_load_bias(const ElfW(Ehdr)* elf); @@ -344,7 +361,8 @@ sonext->next = si; sonext = si; - ns->soinfo_list().push_back(si); + si->generate_handle(); + ns->add_soinfo(si); TRACE("name %s: allocated soinfo @ %p", name, si); return si; @@ -393,9 +411,7 @@ } // remove from the namespace - si->get_namespace()->soinfo_list().remove_if([&](soinfo* candidate) { - return si == candidate; - }); + si->get_namespace()->remove_soinfo(si); si->~soinfo(); g_soinfo_allocator.free(si); @@ -890,6 +906,10 @@ this->namespace_ = ns; } +soinfo::~soinfo() { + g_soinfo_handles_map.erase(handle_); +} + static uint32_t calculate_elf_hash(const char* name) { const uint8_t* name_bytes = reinterpret_cast<const uint8_t*>(name); uint32_t h = 0, g; @@ -1324,21 +1344,21 @@ void* handle) { SymbolName symbol_name(name); - soinfo::soinfo_list_t& soinfo_list = ns->soinfo_list(); - soinfo::soinfo_list_t::iterator start = soinfo_list.begin(); + auto& soinfo_list = ns->soinfo_list(); + auto start = soinfo_list.begin(); if (handle == RTLD_NEXT) { if (caller == nullptr) { return nullptr; } else { - soinfo::soinfo_list_t::iterator it = soinfo_list.find(caller); + auto it = soinfo_list.find(caller); CHECK (it != soinfo_list.end()); start = ++it; } } const ElfW(Sym)* s = nullptr; - for (soinfo::soinfo_list_t::iterator it = start, end = soinfo_list.end(); it != end; ++it) { + for (auto it = start, end = soinfo_list.end(); it != end; ++it) { soinfo* si = *it; // Do not skip RTLD_LOCAL libraries in dlsym(RTLD_DEFAULT, ...) // if the library is opened by application with target api level <= 22 @@ -1718,7 +1738,7 @@ if (si == nullptr) { si = g_public_namespace.find_if(predicate); if (si != nullptr) { - ns->soinfo_list().push_back(si); + ns->add_soinfo(si); } } @@ -1906,7 +1926,7 @@ }); if (candidate != nullptr) { - ns->soinfo_list().push_back(candidate); + ns->add_soinfo(candidate); task->set_soinfo(candidate); return true; } @@ -2274,7 +2294,7 @@ parse_LD_LIBRARY_PATH(ld_library_path); } -soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, +void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr) { soinfo* const caller = find_containing_library(caller_addr); @@ -2318,9 +2338,10 @@ soinfo* si = find_library(ns, name, flags, extinfo, caller); if (si != nullptr) { si->call_constructors(); + return si->to_handle(); } - return si; + return nullptr; } int do_dladdr(const void* addr, Dl_info* info) { @@ -2346,6 +2367,19 @@ return 1; } +static soinfo* soinfo_from_handle(void* handle) { + if ((reinterpret_cast<uintptr_t>(handle) & 1) != 0) { + auto it = g_soinfo_handles_map.find(reinterpret_cast<uintptr_t>(handle)); + if (it == g_soinfo_handles_map.end()) { + return nullptr; + } else { + return it->second; + } + } + + return static_cast<soinfo*>(handle); +} + bool do_dlsym(void* handle, const char* sym_name, const char* sym_ver, void* caller_addr, void** symbol) { #if !defined(__LP64__) @@ -2377,7 +2411,12 @@ if (handle == RTLD_DEFAULT || handle == RTLD_NEXT) { sym = dlsym_linear_lookup(ns, sym_name, vi, &found, caller, handle); } else { - sym = dlsym_handle_lookup(reinterpret_cast<soinfo*>(handle), &found, sym_name, vi); + soinfo* si = soinfo_from_handle(handle); + if (si == nullptr) { + DL_ERR("dlsym failed: invalid handle: %p", handle); + return false; + } + sym = dlsym_handle_lookup(si, &found, sym_name, vi); } if (sym != nullptr) { @@ -2396,9 +2435,16 @@ return false; } -void do_dlclose(soinfo* si) { +int do_dlclose(void* handle) { ProtectedDataGuard guard; + soinfo* si = soinfo_from_handle(handle); + if (si == nullptr) { + DL_ERR("invalid handle: %p", handle); + return -1; + } + soinfo_unload(si); + return 0; } bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) { @@ -2485,12 +2531,10 @@ if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) { // If shared - clone the caller namespace - auto& soinfo_list = caller_ns->soinfo_list(); - std::copy(soinfo_list.begin(), soinfo_list.end(), std::back_inserter(ns->soinfo_list())); + ns->add_soinfos(caller_ns->soinfo_list()); } else { // If not shared - copy only the global group - auto global_group = make_global_group(caller_ns); - std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list())); + ns->add_soinfos(make_global_group(caller_ns)); } return ns; @@ -3334,6 +3378,39 @@ return local_group_root_->target_sdk_version_; } +uintptr_t soinfo::get_handle() const { + CHECK(has_min_version(3)); + CHECK(handle_ != 0); + return handle_; +} + +void* soinfo::to_handle() { + if (get_application_target_sdk_version() <= 23 || !has_min_version(3)) { + return this; + } + + return reinterpret_cast<void*>(get_handle()); +} + +void soinfo::generate_handle() { + CHECK(has_min_version(3)); + CHECK(handle_ == 0); // Make sure this is the first call + + // Make sure the handle is unique and does not collide + // with special values which are RTLD_DEFAULT and RTLD_NEXT. + do { + arc4random_buf(&handle_, sizeof(handle_)); + // the least significant bit for the handle is always 1 + // making it easy to test the type of handle passed to + // dl* functions. + handle_ = handle_ | 1; + } while (handle_ == reinterpret_cast<uintptr_t>(RTLD_DEFAULT) || + handle_ == reinterpret_cast<uintptr_t>(RTLD_NEXT) || + g_soinfo_handles_map.find(handle_) != g_soinfo_handles_map.end()); + + g_soinfo_handles_map[handle_] = this; +} + bool soinfo::prelink_image() { /* Extract dynamic section */ ElfW(Word) dynamic_flags = 0; @@ -4288,7 +4365,7 @@ // before get_libdl_info(). solist = get_libdl_info(); sonext = get_libdl_info(); - g_default_namespace.soinfo_list().push_back(get_libdl_info()); + g_default_namespace.add_soinfo(get_libdl_info()); // We have successfully fixed our own relocations. It's safe to run // the main part of the linker now.
diff --git a/linker/linker.h b/linker/linker.h index f5bab84..65a8373 100644 --- a/linker/linker.h +++ b/linker/linker.h
@@ -266,6 +266,7 @@ public: soinfo(android_namespace_t* ns, const char* name, const struct stat* file_stat, off64_t file_offset, int rtld_flags); + ~soinfo(); void call_constructors(); void call_destructors(); @@ -343,6 +344,10 @@ void set_mapped_by_caller(bool reserved_map); bool is_mapped_by_caller() const; + uintptr_t get_handle() const; + void generate_handle(); + void* to_handle(); + private: bool elf_lookup(SymbolName& symbol_name, const version_info* vi, uint32_t* symbol_index) const; ElfW(Sym)* elf_addr_lookup(const void* addr); @@ -407,6 +412,7 @@ // version >= 3 std::vector<std::string> dt_runpath_; android_namespace_t* namespace_; + uintptr_t handle_; friend soinfo* get_libdl_info(); }; @@ -429,8 +435,8 @@ void do_android_get_LD_LIBRARY_PATH(char*, size_t); void do_android_update_LD_LIBRARY_PATH(const char* ld_library_path); -soinfo* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr); -void do_dlclose(soinfo* si); +void* do_dlopen(const char* name, int flags, const android_dlextinfo* extinfo, void* caller_addr); +int do_dlclose(void* handle); int do_dl_iterate_phdr(int (*cb)(dl_phdr_info* info, size_t size, void* data), void* data);
diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 66d8859..bbdc024 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp
@@ -1050,3 +1050,19 @@ ASSERT_TRUE(ns_get_dlopened_string_anon() != ns_get_dlopened_string_private()); } + +TEST(dlext, dlopen_handle_value_platform) { + void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE((reinterpret_cast<uintptr_t>(handle) & 1) != 0) + << "dlopen should return odd value for the handle"; + dlclose(handle); +} + +TEST(dlext, dlopen_handle_value_app_compat) { + android_set_application_target_sdk_version(23); + void* handle = dlopen("libtest_dlsym_from_this.so", RTLD_NOW | RTLD_LOCAL); + ASSERT_TRUE(reinterpret_cast<uintptr_t>(handle) % sizeof(uintptr_t) == 0) + << "dlopen should return valid pointer"; + dlclose(handle); +} +