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);