From: Dimitry Ivanov Date: Thu, 12 May 2016 22:20:21 +0000 (-0700) Subject: linker: Allow caller to specify parent namespace X-Git-Tag: android-x86-7.1-r1~40^2~10 X-Git-Url: http://git.osdn.net/view?a=commitdiff_plain;h=49cfc899a3708fae9175e44c0c02cd479fda8b36;p=android-x86%2Fbionic.git linker: Allow caller to specify parent namespace This change enables apps to share libraries opened with RTLD_GLOBAL between different classloader namespaces. The new parameter to create_namespace allows native_loader to instruct the linker to share libraries belonging to global group from a specified namespace instead of using the caller_ns. Bug: http://b/28560538 Bug: https://code.google.com/p/android/issues/detail?id=208458 Change-Id: I5d0c62730bbed19cdeb16c7559c74aa262a2475f (cherry picked from commit fc2da53440383fe1026e0eb287d643b577c2707d) --- diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index ce9fbe6b8..12dd03939 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -153,15 +153,22 @@ bool android_init_namespaces(const char* public_ns_sonames, return success; } -android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, - const char* default_library_path, uint64_t type, - const char* permitted_when_isolated_path) { +android_namespace_t* android_create_namespace(const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent_namespace) { void* caller_addr = __builtin_return_address(0); ScopedPthreadMutexLocker locker(&g_dl_mutex); - android_namespace_t* result = create_namespace(caller_addr, name, ld_library_path, - default_library_path, type, - permitted_when_isolated_path); + android_namespace_t* result = create_namespace(caller_addr, + name, + ld_library_path, + default_library_path, + type, + permitted_when_isolated_path, + parent_namespace); if (result == nullptr) { __bionic_format_dlerror("android_create_namespace failed", linker_get_error_buffer()); diff --git a/linker/linker.cpp b/linker/linker.cpp index d21625edb..a29a599f5 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -139,6 +139,7 @@ struct android_namespace_t { }; android_namespace_t g_default_namespace; + static std::unordered_map g_soinfo_handles_map; static android_namespace_t* g_anonymous_namespace = &g_default_namespace; @@ -1989,6 +1990,26 @@ static soinfo::soinfo_list_t make_global_group(android_namespace_t* ns) { return global_group; } +// This function provides a list of libraries to be shared +// by the namespace. For the default namespace this is the global +// group (see make_global_group). For all others this is a group +// of RTLD_GLOBAL libraries (which includes the global group from +// the default namespace). +static soinfo::soinfo_list_t get_shared_group(android_namespace_t* ns) { + if (ns == &g_default_namespace) { + return make_global_group(ns); + } + + soinfo::soinfo_list_t shared_group; + ns->soinfo_list().for_each([&](soinfo* si) { + if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) { + shared_group.push_back(si); + } + }); + + return shared_group; +} + static void shuffle(std::vector* v) { for (size_t i = 0, size = v->size(); i < size; ++i) { size_t n = size - i; @@ -2531,7 +2552,7 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_ // 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); + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr); if (anon_ns == nullptr) { g_public_namespace_initialized = false; @@ -2547,7 +2568,8 @@ android_namespace_t* create_namespace(const void* caller_addr, const char* ld_library_path, const char* default_library_path, uint64_t type, - const char* permitted_when_isolated_path) { + 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."); return nullptr; @@ -2559,6 +2581,11 @@ android_namespace_t* create_namespace(const void* caller_addr, caller_soinfo->get_primary_namespace() : g_anonymous_namespace; + // if parent_namespace is nullptr -> set it to the caller namespace + if (parent_namespace == nullptr) { + parent_namespace = caller_ns; + } + ProtectedDataGuard guard; std::vector ld_library_paths; std::vector default_library_paths; @@ -2576,11 +2603,11 @@ android_namespace_t* create_namespace(const void* caller_addr, ns->set_permitted_paths(std::move(permitted_paths)); if ((type & ANDROID_NAMESPACE_TYPE_SHARED) != 0) { - // If shared - clone the caller namespace - ns->add_soinfos(caller_ns->soinfo_list()); + // If shared - clone the parent namespace + ns->add_soinfos(parent_namespace->soinfo_list()); } else { - // If not shared - copy only the global group - ns->add_soinfos(make_global_group(caller_ns)); + // If not shared - copy only the shared group + ns->add_soinfos(get_shared_group(parent_namespace)); } return ns; diff --git a/linker/linker.h b/linker/linker.h index 3e6fa88ca..3ea601fa2 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -495,8 +495,12 @@ enum { }; bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path); -android_namespace_t* create_namespace(const void* caller_addr, const char* name, - const char* ld_library_path, const char* default_library_path, - uint64_t type, const char* permitted_when_isolated_path); +android_namespace_t* create_namespace(const void* caller_addr, + const char* name, + const char* ld_library_path, + const char* default_library_path, + uint64_t type, + const char* permitted_when_isolated_path, + android_namespace_t* parent_namespace); #endif diff --git a/tests/dlext_private.h b/tests/dlext_private.h index 8eb86ca48..049db91a8 100644 --- a/tests/dlext_private.h +++ b/tests/dlext_private.h @@ -83,7 +83,8 @@ extern struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, const char* default_library_path, uint64_t type, - const char* permitted_when_isolated_path); + const char* permitted_when_isolated_path, + android_namespace_t* parent); extern void android_set_application_target_sdk_version(uint32_t target); diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 205ad410f..49df57bb3 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -648,13 +648,13 @@ TEST(dlext, ns_smoke) { android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), - ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr); ASSERT_TRUE(ns1 != nullptr) << dlerror(); android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), - ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr); + ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr, nullptr); ASSERT_TRUE(ns2 != nullptr) << dlerror(); // This should not have affect search path for default namespace: @@ -756,19 +756,25 @@ TEST(dlext, ns_isolated) { android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), - ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr); ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror(); android_namespace_t* ns_isolated = - android_create_namespace("private_isolated1", nullptr, + android_create_namespace("private_isolated1", + nullptr, (lib_path + "/private_namespace_libs").c_str(), - ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr); + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, + nullptr); ASSERT_TRUE(ns_isolated != nullptr) << dlerror(); android_namespace_t* ns_isolated2 = android_create_namespace("private_isolated2", (lib_path + "/private_namespace_libs").c_str(), - nullptr, ANDROID_NAMESPACE_TYPE_ISOLATED, lib_path.c_str()); + nullptr, + ANDROID_NAMESPACE_TYPE_ISOLATED, + lib_path.c_str(), + nullptr); ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror(); ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); @@ -863,14 +869,14 @@ TEST(dlext, ns_shared) { android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), - ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr); ASSERT_TRUE(ns_not_isolated != 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); + nullptr, nullptr); ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror(); ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); @@ -977,7 +983,7 @@ TEST(dlext, ns_shared_dlclose) { android_create_namespace("private_isolated_shared", nullptr, (lib_path + "/private_namespace_libs").c_str(), ANDROID_NAMESPACE_TYPE_ISOLATED | ANDROID_NAMESPACE_TYPE_SHARED, - nullptr); + nullptr, nullptr); ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror(); // Check if "libnstest_dlopened.so" is loaded (and the same) @@ -1022,6 +1028,78 @@ TEST(dlext, ns_shared_dlclose) { << "Error: " << g_public_lib << " is accessible in shared namespace"; } +TEST(dlext, ns_isolated_rtld_global) { + static const char* root_lib = "libnstest_root.so"; + std::string path = "libc.so:libc++.so:libdl.so:libm.so"; + + ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)); + + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH; + + const std::string lib_public_path = lib_path + "/public_namespace_libs"; + + android_namespace_t* ns1 = + android_create_namespace("isolated1", + nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_ISOLATED, + lib_public_path.c_str(), + nullptr); + ASSERT_TRUE(ns1 != nullptr) << dlerror(); + + android_namespace_t* ns2 = + android_create_namespace("isolated2", + nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_ISOLATED, + lib_public_path.c_str(), + nullptr); + ASSERT_TRUE(ns2 != nullptr) << dlerror(); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns1; + + void* handle_global = android_dlopen_ext((lib_public_path + "/" + g_public_lib).c_str(), + RTLD_GLOBAL, + &extinfo); + + ASSERT_TRUE(handle_global != nullptr) << dlerror(); + + android_namespace_t* ns1_child = + android_create_namespace("isolated1_child", + nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_ISOLATED, + nullptr, + ns1); + + // Now - only ns1 and ns1 child should be able to dlopen root_lib + // attempt to use ns2 should result in dlerror() + + // Check ns1_child first. + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns1_child; + + void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle1 != nullptr) << dlerror(); + + // now ns1 + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns1; + + handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle1 != nullptr) << dlerror(); + + // and ns2 should fail + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns2; + + handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle1 == nullptr); + ASSERT_STREQ("dlopen failed: library \"libnstest_public.so\" not found", dlerror()); +} + 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; @@ -1039,7 +1117,7 @@ TEST(dlext, ns_anonymous) { android_namespace_t* ns = android_create_namespace( "private", nullptr, (lib_path + "/private_namespace_libs").c_str(), - ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr, nullptr); ASSERT_TRUE(ns != nullptr) << dlerror();