Replace public library list with shared lib sonames (part 2/2)
This commit updates interface of libdl.c.
1. android_init_namespaces is replaces with android_init_anonymous_namespace
2. added 2 arguments to android_create_namespace to specify linked namespace
and the list of shared libraries sonames.
3. symbol lookup does not get past boundary libraries (added check and test for it).
Bug: http://b/26833548
Bug: http://b/21879602
Test: bionic-unit-tests --gtest_filter=dl*:Dl*
Change-Id: I32921da487a02e5bd0d2fc528904d1228394bfb9
diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp
index 0092179..4e8a364 100644
--- a/linker/dlfcn.cpp
+++ b/linker/dlfcn.cpp
@@ -30,8 +30,6 @@
#include "private/ScopedPthreadMutexLocker.h"
#include "private/ThreadLocalBuffer.h"
-/* This file hijacks the symbols stubbed out in libdl.so. */
-
static pthread_mutex_t g_dl_mutex = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
static char* __bionic_set_dlerror(char* new_value) {
@@ -155,12 +153,12 @@
get_dlwarning(obj, f);
}
-bool __android_init_namespaces(const char* public_ns_sonames,
- const char* anon_ns_library_path) {
+bool __android_init_anonymous_namespace(const char* shared_libs_sonames,
+ const char* library_search_path) {
ScopedPthreadMutexLocker locker(&g_dl_mutex);
- bool success = init_namespaces(public_ns_sonames, anon_ns_library_path);
+ bool success = init_anonymous_namespace(shared_libs_sonames, library_search_path);
if (!success) {
- __bionic_format_dlerror("android_init_namespaces failed", linker_get_error_buffer());
+ __bionic_format_dlerror("android_init_anonymous_namespace failed", linker_get_error_buffer());
}
return success;
@@ -190,6 +188,20 @@
return result;
}
+bool __android_link_namespaces(android_namespace_t* namespace_from,
+ android_namespace_t* namespace_to,
+ const char* shared_libs_sonames) {
+ ScopedPthreadMutexLocker locker(&g_dl_mutex);
+
+ bool success = link_namespaces(namespace_from, namespace_to, shared_libs_sonames);
+
+ if (!success) {
+ __bionic_format_dlerror("android_link_namespaces failed", linker_get_error_buffer());
+ }
+
+ return success;
+}
+
void __cfi_fail(uint64_t CallSiteTypeId, void* Ptr, void *DiagData, void *CallerPc) {
CFIShadowWriter::CfiFail(CallSiteTypeId, Ptr, DiagData, CallerPc);
}
@@ -226,15 +238,15 @@
// 01234567890 1234567890123456789012345678901234567890123456789012 3456789012345678901234567890123456789
"dlopen_ext\0__loader_android_set_application_target_sdk_version\0__loader_android_get_application_targ"
// 3*
- // 000000000011111 111112222222222333333333344444444 4455555555556666666666777777777788 8888888899999999 99
- // 012345678901234 567890123456789012345678901234567 8901234567890123456789012345678901 2345678901234567 89
- "et_sdk_version\0__loader_android_init_namespaces\0__loader_android_create_namespace\0__loader_dlvsym\0__"
+ // 000000000011111 111112222222222333333333344444444445555555 5556666666666777777777788888888889 999999999
+ // 012345678901234 567890123456789012345678901234567890123456 7890123456789012345678901234567890 123456789
+ "et_sdk_version\0__loader_android_init_anonymous_namespace\0__loader_android_create_namespace\0__loader_"
// 4*
- // 0000000000111111111122222 222223333333333444 4444444555555555566666666667777 77777788888888889999999999
- // 0123456789012345678901234 567890123456789012 3456789012345678901234567890123 45678901234567890123456789
- "loader_android_dlwarning\0__loader_cfi_fail\0"
+ // 0000000 000111111111122222222223333 333333444444444455 555555556666666666777777777788888 888889999999999
+ // 0123456 789012345678901234567890123 456789012345678901 234567890123456789012345678901234 567890123456789
+ "dlvsym\0__loader_android_dlwarning\0__loader_cfi_fail\0__loader_android_link_namespaces\0"
#if defined(__arm__)
- // 443
+ // 485
"__loader_dl_unwind_find_exidx\0"
#endif
;
@@ -256,13 +268,14 @@
ELFW(SYM_INITIALIZER)(183, &__android_dlopen_ext, 1),
ELFW(SYM_INITIALIZER)(211, &__android_set_application_target_sdk_version, 1),
ELFW(SYM_INITIALIZER)(263, &__android_get_application_target_sdk_version, 1),
- ELFW(SYM_INITIALIZER)(315, &__android_init_namespaces, 1),
- ELFW(SYM_INITIALIZER)(348, &__android_create_namespace, 1),
- ELFW(SYM_INITIALIZER)(382, &__dlvsym, 1),
- ELFW(SYM_INITIALIZER)(398, &__android_dlwarning, 1),
- ELFW(SYM_INITIALIZER)(425, &__cfi_fail, 1),
+ ELFW(SYM_INITIALIZER)(315, &__android_init_anonymous_namespace, 1),
+ ELFW(SYM_INITIALIZER)(357, &__android_create_namespace, 1),
+ ELFW(SYM_INITIALIZER)(391, &__dlvsym, 1),
+ ELFW(SYM_INITIALIZER)(407, &__android_dlwarning, 1),
+ ELFW(SYM_INITIALIZER)(434, &__cfi_fail, 1),
+ ELFW(SYM_INITIALIZER)(452, &__android_link_namespaces, 1),
#if defined(__arm__)
- ELFW(SYM_INITIALIZER)(443, &__dl_unwind_find_exidx, 1),
+ ELFW(SYM_INITIALIZER)(485, &__dl_unwind_find_exidx, 1),
#endif
};
@@ -279,9 +292,9 @@
// Note that adding any new symbols here requires stubbing them out in libdl.
static unsigned g_libdl_buckets[1] = { 1 };
#if defined(__arm__)
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 0 };
#else
-static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 0 };
+static unsigned g_libdl_chains[] = { 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 0 };
#endif
static uint8_t __libdl_info_buf[sizeof(soinfo)] __attribute__((aligned(8)));
diff --git a/linker/linker.cpp b/linker/linker.cpp
index 522d5dc..8074323 100644
--- a/linker/linker.cpp
+++ b/linker/linker.cpp
@@ -185,10 +185,7 @@
static const char* const* g_default_ld_paths;
static std::vector<std::string> g_ld_preload_names;
-static bool g_public_namespace_initialized;
-
-// TODO (dimitry): Remove once interface between libnativeloader and the linker is updated
-static std::unordered_set<std::string> g_public_namespace_sonames;
+static bool g_anonymous_namespace_initialized;
#if STATS
struct linker_stats_t {
@@ -656,11 +653,18 @@
typedef linked_list_t<const char> StringLinkedList;
typedef std::vector<LoadTask*> LoadTaskList;
+enum walk_action_result_t : uint32_t {
+ kWalkStop = 0,
+ kWalkContinue = 1,
+ kWalkSkip = 2
+};
// This function walks down the tree of soinfo dependencies
// in breadth-first order and
// * calls action(soinfo* si) for each node, and
-// * terminates walk if action returns false.
+// * terminates walk if action returns kWalkStop
+// * skips children of the node if action
+// return kWalkSkip
//
// walk_dependencies_tree returns false if walk was terminated
// by the action and true otherwise.
@@ -679,23 +683,30 @@
continue;
}
- if (!action(si)) {
+ walk_action_result_t result = action(si);
+
+ if (result == kWalkStop) {
return false;
}
visited.push_back(si);
- si->get_children().for_each([&](soinfo* child) {
- visit_list.push_back(child);
- });
+ if (result != kWalkSkip) {
+ si->get_children().for_each([&](soinfo* child) {
+ visit_list.push_back(child);
+ });
+ }
}
return true;
}
-static const ElfW(Sym)* dlsym_handle_lookup(soinfo* root, soinfo* skip_until,
- soinfo** found, SymbolName& symbol_name,
+static const ElfW(Sym)* dlsym_handle_lookup(android_namespace_t* ns,
+ soinfo* root,
+ soinfo* skip_until,
+ soinfo** found,
+ SymbolName& symbol_name,
const version_info* vi) {
const ElfW(Sym)* result = nullptr;
bool skip_lookup = skip_until != nullptr;
@@ -703,20 +714,24 @@
walk_dependencies_tree(&root, 1, [&](soinfo* current_soinfo) {
if (skip_lookup) {
skip_lookup = current_soinfo != skip_until;
- return true;
+ return kWalkContinue;
+ }
+
+ if (!ns->is_accessible(current_soinfo)) {
+ return kWalkSkip;
}
if (!current_soinfo->find_symbol_by_name(symbol_name, vi, &result)) {
result = nullptr;
- return false;
+ return kWalkStop;
}
if (result != nullptr) {
*found = current_soinfo;
- return false;
+ return kWalkStop;
}
- return true;
+ return kWalkContinue;
});
return result;
@@ -731,8 +746,10 @@
// This is used by dlsym(3). It performs symbol lookup only within the
// specified soinfo object and its dependencies in breadth first order.
-static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si, soinfo** found,
- const char* name, const version_info* vi) {
+static const ElfW(Sym)* dlsym_handle_lookup(soinfo* si,
+ soinfo** found,
+ const char* name,
+ const version_info* vi) {
// According to man dlopen(3) and posix docs in the case when si is handle
// of the main executable we need to search not only in the executable and its
// dependencies but also in all libraries loaded with RTLD_GLOBAL.
@@ -745,7 +762,11 @@
}
SymbolName symbol_name(name);
- return dlsym_handle_lookup(si, nullptr, found, symbol_name, vi);
+ // note that the namespace is not the namespace associated with caller_addr
+ // we use ns associated with root si intentionally here. Using caller_ns
+ // causes problems when user uses dlopen_ext to open a library in the separate
+ // namespace and then calls dlsym() on the handle.
+ return dlsym_handle_lookup(si->get_primary_namespace(), si, nullptr, found, symbol_name, vi);
}
/* This is used by dlsym(3) to performs a global symbol lookup. If the
@@ -800,8 +821,14 @@
// case we already did it.
if (s == nullptr && caller != nullptr &&
(caller->get_rtld_flags() & RTLD_GLOBAL) == 0) {
- return dlsym_handle_lookup(caller->get_local_group_root(),
- (handle == RTLD_NEXT) ? caller : nullptr, found, symbol_name, vi);
+ soinfo* local_group_root = caller->get_local_group_root();
+
+ return dlsym_handle_lookup(local_group_root->get_primary_namespace(),
+ local_group_root,
+ (handle == RTLD_NEXT) ? caller : nullptr,
+ found,
+ symbol_name,
+ vi);
}
if (s != nullptr) {
@@ -1542,8 +1569,12 @@
(start_with != nullptr && add_as_children) ? &start_with : soinfos,
(start_with != nullptr && add_as_children) ? 1 : soinfos_count,
[&] (soinfo* si) {
- local_group.push_back(si);
- return true;
+ if (ns->is_accessible(si)) {
+ local_group.push_back(si);
+ return kWalkContinue;
+ } else {
+ return kWalkSkip;
+ }
});
bool linked = local_group.visit([&](soinfo* si) {
@@ -2001,39 +2032,42 @@
return 0;
}
-bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path) {
- if (g_public_namespace_initialized) {
- DL_ERR("public namespace has already been initialized.");
+bool init_anonymous_namespace(const char* shared_lib_sonames, const char* library_search_path) {
+ if (g_anonymous_namespace_initialized) {
+ DL_ERR("anonymous namespace has already been initialized.");
return false;
}
- if (public_ns_sonames == nullptr || public_ns_sonames[0] == '\0') {
- DL_ERR("error initializing public namespace: the list of public libraries is empty.");
- return false;
- }
-
- auto sonames = android::base::Split(public_ns_sonames, ":");
-
ProtectedDataGuard guard;
- g_public_namespace_sonames = std::unordered_set<std::string>(sonames.begin(), sonames.end());
-
- g_public_namespace_initialized = true;
+ g_anonymous_namespace_initialized = true;
// create anonymous namespace
// When the caller is nullptr - create_namespace will take global group
// from the anonymous namespace, which is fine because anonymous namespace
// is still pointing to the default one.
android_namespace_t* anon_ns =
- create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path,
- ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, &g_default_namespace);
+ create_namespace(nullptr,
+ "(anonymous)",
+ nullptr,
+ library_search_path,
+ // TODO (dimitry): change to isolated eventually.
+ ANDROID_NAMESPACE_TYPE_REGULAR,
+ nullptr,
+ &g_default_namespace);
if (anon_ns == nullptr) {
- g_public_namespace_initialized = false;
+ g_anonymous_namespace_initialized = false;
+ return false;
+ }
+
+ if (!link_namespaces(anon_ns, &g_default_namespace, shared_lib_sonames)) {
+ g_anonymous_namespace_initialized = false;
return false;
}
g_anonymous_namespace = anon_ns;
+
return true;
}
@@ -2051,8 +2085,8 @@
uint64_t type,
const char* permitted_when_isolated_path,
android_namespace_t* parent_namespace) {
- if (!g_public_namespace_initialized) {
- DL_ERR("cannot create namespace: public namespace is not initialized.");
+ if (!g_anonymous_namespace_initialized) {
+ DL_ERR("cannot create namespace: anonymous namespace is not initialized.");
return nullptr;
}
@@ -2089,13 +2123,36 @@
add_soinfos_to_namespace(get_shared_group(parent_namespace), ns);
}
- // link it to default namespace
- // TODO (dimitry): replace this with user-supplied link once interface is updated
- ns->add_linked_namespace(&g_default_namespace, g_public_namespace_sonames);
-
return ns;
}
+bool link_namespaces(android_namespace_t* namespace_from,
+ android_namespace_t* namespace_to,
+ const char* shared_lib_sonames) {
+ if (namespace_to == nullptr) {
+ namespace_to = &g_default_namespace;
+ }
+
+ if (namespace_from == nullptr) {
+ DL_ERR("error linking namespaces: namespace_from is null.");
+ return false;
+ }
+
+ if (shared_lib_sonames == nullptr || shared_lib_sonames[0] == '\0') {
+ DL_ERR("error linking namespaces \"%s\"->\"%s\": the list of shared libraries is empty.",
+ namespace_from->get_name(), namespace_to->get_name());
+ return false;
+ }
+
+ auto sonames = android::base::Split(shared_lib_sonames, ":");
+ std::unordered_set<std::string> sonames_set(sonames.begin(), sonames.end());
+
+ ProtectedDataGuard guard;
+ namespace_from->add_linked_namespace(namespace_to, sonames_set);
+
+ return true;
+}
+
ElfW(Addr) call_ifunc_resolver(ElfW(Addr) resolver_addr) {
typedef ElfW(Addr) (*ifunc_resolver_t)(void);
ifunc_resolver_t ifunc_resolver = reinterpret_cast<ifunc_resolver_t>(resolver_addr);
diff --git a/linker/linker.h b/linker/linker.h
index 7746982..d5d4980 100644
--- a/linker/linker.h
+++ b/linker/linker.h
@@ -160,7 +160,7 @@
ANDROID_NAMESPACE_TYPE_ISOLATED,
};
-bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path);
+bool init_anonymous_namespace(const char* shared_lib_sonames, const char* library_search_path);
android_namespace_t* create_namespace(const void* caller_addr,
const char* name,
const char* ld_library_path,
@@ -169,4 +169,8 @@
const char* permitted_when_isolated_path,
android_namespace_t* parent_namespace);
+bool link_namespaces(android_namespace_t* namespace_from,
+ android_namespace_t* namespace_to,
+ const char* shared_lib_sonames);
+
#endif
diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp
index 675f324..a7e14cc 100644
--- a/linker/linker_namespaces.cpp
+++ b/linker/linker_namespaces.cpp
@@ -27,6 +27,7 @@
*/
#include "linker_namespaces.h"
+#include "linker_soinfo.h"
#include "linker_utils.h"
#include <vector>
@@ -57,3 +58,23 @@
return false;
}
+bool android_namespace_t::is_accessible(soinfo* s) {
+ std::vector<soinfo*> soinfos;
+ soinfos.push_back(s);
+ s->get_parents().for_each([&](soinfo* parent_si) {
+ soinfos.push_back(parent_si);
+ });
+
+ return std::find_if(soinfos.begin(), soinfos.end(), [this](soinfo* si) {
+ if (si->get_primary_namespace() == this) {
+ return true;
+ }
+
+ const android_namespace_list_t& secondary_namespaces = si->get_secondary_namespaces();
+ if (secondary_namespaces.find(this) != secondary_namespaces.end()) {
+ return true;
+ }
+
+ return false;
+ }) != soinfos.end();
+}
diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h
index 17800c6..868b4a6 100644
--- a/linker/linker_namespaces.h
+++ b/linker/linker_namespaces.h
@@ -118,6 +118,11 @@
// always returns true for not isolated namespace.
bool is_accessible(const std::string& path);
+ // Returns true if si is accessible from this namespace. A soinfo
+ // is considered accessible when it belongs to this namespace
+ // or one of it's parent soinfos belongs to this namespace.
+ bool is_accessible(soinfo* si);
+
private:
const char* name_;
bool is_isolated_;
diff --git a/linker/linker_soinfo.cpp b/linker/linker_soinfo.cpp
index 16e42c2..6601dc1 100644
--- a/linker/linker_soinfo.cpp
+++ b/linker/linker_soinfo.cpp
@@ -634,6 +634,11 @@
secondary_namespaces_.push_back(secondary_ns);
}
+android_namespace_list_t& soinfo::get_secondary_namespaces() {
+ CHECK(has_min_version(3));
+ return secondary_namespaces_;
+}
+
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);
@@ -695,7 +700,6 @@
return local_group_root_;
}
-
void soinfo::set_mapped_by_caller(bool mapped_by_caller) {
if (mapped_by_caller) {
flags_ |= FLAG_MAPPED_BY_CALLER;
diff --git a/linker/linker_soinfo.h b/linker/linker_soinfo.h
index 7ef5da2..71eb543 100644
--- a/linker/linker_soinfo.h
+++ b/linker/linker_soinfo.h
@@ -263,6 +263,7 @@
const std::vector<std::string>& get_dt_runpath() const;
android_namespace_t* get_primary_namespace();
void add_secondary_namespace(android_namespace_t* secondary_ns);
+ android_namespace_list_t& get_secondary_namespaces();
void set_mapped_by_caller(bool reserved_map);
bool is_mapped_by_caller() const;