diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index 7979c43d4..d5ec38690 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -142,6 +142,33 @@ extern void* android_dlopen_ext(const char* filename, int flag, const android_dl extern bool android_init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path); + +enum { + /* A regular namespace is the namespace with a custom search path that does + * not impose any restrictions on the location of native libraries. + */ + ANDROID_NAMESPACE_TYPE_REGULAR = 0, + + /* An isolated namespace requires all the libraries to be on the search path + * or under permitted_when_isolated_path. The search path is the union of + * ld_library_path and default_library_path. + */ + ANDROID_NAMESPACE_TYPE_ISOLATED = 1, + + /* The shared namespace clones the list of libraries of the caller namespace upon creation + * which means that they are shared between namespaces - the caller namespace and the new one + * will use the same copy of a library if it was loaded prior to android_create_namespace call. + * + * Note that libraries loaded after the namespace is created will not be shared. + * + * Shared namespaces can be isolated or regular. Note that they do not inherit the search path nor + * permitted_path from the caller's namespace. + */ + ANDROID_NAMESPACE_TYPE_SHARED = 2, + ANDROID_NAMESPACE_TYPE_SHARED_ISOLATED = ANDROID_NAMESPACE_TYPE_SHARED | + ANDROID_NAMESPACE_TYPE_ISOLATED, +}; + /* * Creates new linker namespace. * ld_library_path and default_library_path represent the search path @@ -152,19 +179,19 @@ extern bool android_init_namespaces(const char* public_ns_sonames, * 2. In directories specified by DT_RUNPATH of the "needed by" binary. * 3. deault_library_path (This of this as namespace-local default library path) * - * When is_isolated is true the resulting namespace requires all of the libraries - * to be on the search path or under the permitted_when_isolated_path; the search_path is - * ld_library_path:default_library_path. Note that the permitted_when_isolated_path path - * is not part of the search_path and does not affect the search order. It is a way - * to allow loading libraries from specific locations when using absolute path. - * + * When type is ANDROID_NAMESPACE_TYPE_ISOLATED the resulting namespace requires all of + * the libraries to be on the search path or under the permitted_when_isolated_path; + * the search_path is ld_library_path:default_library_path. Note that the + * permitted_when_isolated_path path is not part of the search_path and + * does not affect the search order. It is a way to allow loading libraries from specific + * locations when using absolute path. * If a library or any of its dependencies are outside of the permitted_when_isolated_path * and search_path, and it is not part of the public namespace dlopen will fail. */ extern struct android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, const char* default_library_path, - bool is_isolated, + uint64_t type, const char* permitted_when_isolated_path); __END_DECLS diff --git a/libdl/libdl.c b/libdl/libdl.c index 0604d3eee..fa5237f6f 100644 --- a/libdl/libdl.c +++ b/libdl/libdl.c @@ -65,7 +65,7 @@ bool android_init_namespaces(const char* public_ns_sonames __unused, struct android_namespace_t* android_create_namespace(const char* name __unused, const char* ld_library_path __unused, const char* default_library_path __unused, - bool isolated __unused, + uint64_t type __unused, const char* permitted_when_isolated_path __unused) { return 0; } diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index ba54d3973..a7c3fb0cb 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -148,12 +148,14 @@ bool android_init_namespaces(const char* public_ns_sonames, } android_namespace_t* android_create_namespace(const char* name, const char* ld_library_path, - const char* default_library_path, bool is_isolated, + const char* default_library_path, uint64_t type, const char* permitted_when_isolated_path) { + void* caller_addr = __builtin_return_address(0); ScopedPthreadMutexLocker locker(&g_dl_mutex); - android_namespace_t* result = create_namespace(name, ld_library_path, default_library_path, - is_isolated, permitted_when_isolated_path); + android_namespace_t* result = create_namespace(caller_addr, name, ld_library_path, + default_library_path, type, + permitted_when_isolated_path); 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 e38e2523c..eb938c750 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1747,7 +1747,6 @@ static bool load_library(android_namespace_t* ns, }); return true; - } static bool load_library(android_namespace_t* ns, @@ -2377,8 +2376,12 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_ g_public_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("(anonymous)", nullptr, anon_ns_library_path, false, nullptr); + create_namespace(nullptr, "(anonymous)", nullptr, anon_ns_library_path, + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); if (anon_ns == nullptr) { g_public_namespace_initialized = false; @@ -2389,16 +2392,23 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_ return true; } -android_namespace_t* create_namespace(const char* name, +android_namespace_t* create_namespace(const void* caller_addr, + const char* name, const char* ld_library_path, const char* default_library_path, - bool is_isolated, + uint64_t type, const char* permitted_when_isolated_path) { if (!g_public_namespace_initialized) { DL_ERR("cannot create namespace: public namespace is not initialized."); return nullptr; } + soinfo* caller_soinfo = find_containing_library(caller_addr); + + android_namespace_t* caller_ns = caller_soinfo != nullptr ? + caller_soinfo->get_namespace() : + g_anonymous_namespace; + ProtectedDataGuard guard; std::vector ld_library_paths; std::vector default_library_paths; @@ -2410,14 +2420,20 @@ android_namespace_t* create_namespace(const char* name, android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t(); ns->set_name(name); - ns->set_isolated(is_isolated); + ns->set_isolated((type & ANDROID_NAMESPACE_TYPE_ISOLATED) != 0); ns->set_ld_library_paths(std::move(ld_library_paths)); ns->set_default_library_paths(std::move(default_library_paths)); ns->set_permitted_paths(std::move(permitted_paths)); - // TODO(dimtiry): Should this be global group of caller's namespace? - auto global_group = make_global_group(&g_default_namespace); - std::copy(global_group.begin(), global_group.end(), std::back_inserter(ns->soinfo_list())); + 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())); + } 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())); + } return ns; } diff --git a/linker/linker.h b/linker/linker.h index 5ec259ede..5a06853db 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -444,8 +444,8 @@ void set_application_target_sdk_version(uint32_t target); uint32_t get_application_target_sdk_version(); bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_path); -android_namespace_t* create_namespace(const char* name, const char* ld_library_path, - const char* default_library_path, bool is_isolated, - 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); #endif diff --git a/linker/linker_debug.h b/linker/linker_debug.h index 17c6986f0..5af992916 100644 --- a/linker/linker_debug.h +++ b/linker/linker_debug.h @@ -55,6 +55,7 @@ *********************************************************************/ #include "private/libc_logging.h" +#include __LIBC_HIDDEN__ extern int g_ld_debug_verbosity; diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 5327e36b2..261aa5549 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -625,13 +625,21 @@ TEST(dlext, ns_smoke) { // Check that libraries added to public namespace are NODELETE dlclose(handle_public); - handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), RTLD_NOW | RTLD_NOLOAD); + handle_public = dlopen((lib_path + "/public_namespace_libs/" + g_public_lib).c_str(), + RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle_public != nullptr) << dlerror(); - android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, nullptr); + android_namespace_t* ns1 = + android_create_namespace("private", nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); ASSERT_TRUE(ns1 != nullptr) << dlerror(); - android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, nullptr); + android_namespace_t* ns2 = + android_create_namespace("private_isolated", nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_ISOLATED, nullptr); ASSERT_TRUE(ns2 != nullptr) << dlerror(); // This should not have affect search path for default namespace: @@ -732,13 +740,22 @@ TEST(dlext, ns_isolated) { ASSERT_TRUE(android_init_namespaces(path.c_str(), nullptr)) << dlerror(); - android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, nullptr); + android_namespace_t* ns_not_isolated = + android_create_namespace("private", nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); ASSERT_TRUE(ns_not_isolated != nullptr) << dlerror(); - android_namespace_t* ns_isolated = android_create_namespace("private_isolated1", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, nullptr); + android_namespace_t* ns_isolated = + android_create_namespace("private_isolated1", nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_ISOLATED, 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, true, lib_path.c_str()); + 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()); ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror(); ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); @@ -808,6 +825,122 @@ TEST(dlext, ns_isolated) { dlclose(handle1); } +TEST(dlext, ns_shared) { + static const char* root_lib = "libnstest_root_not_isolated.so"; + static const char* root_lib_isolated = "libnstest_root.so"; + std::string path = std::string("libc.so:libc++.so:libdl.so:libm.so:") + g_public_lib; + + const std::string lib_path = std::string(getenv("ANDROID_DATA")) + NATIVE_TESTS_PATH; + 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(); + + 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_not_isolated = + android_create_namespace("private", nullptr, + (lib_path + "/private_namespace_libs").c_str(), + ANDROID_NAMESPACE_TYPE_REGULAR, 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); + ASSERT_TRUE(ns_isolated_shared != nullptr) << dlerror(); + + ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); + ASSERT_STREQ("dlopen failed: library \"libnstest_root_not_isolated.so\" not found", dlerror()); + + std::string lib_private_external_path = + lib_path + "/private_namespace_libs_external/libnstest_private_external.so"; + + // Load lib_private_external_path to default namespace + // (it should remain invisible for the isolated namespaces after this) + void* handle = dlopen(lib_private_external_path.c_str(), RTLD_NOW); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + android_dlextinfo extinfo; + extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns_not_isolated; + + void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle1 != nullptr) << dlerror(); + + extinfo.library_namespace = ns_isolated_shared; + + void* handle2 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 == nullptr); + ASSERT_STREQ("dlopen failed: library \"libnstest_private_external.so\" not found", dlerror()); + + // Check dlopen by absolute path + handle2 = android_dlopen_ext(lib_private_external_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle2 == nullptr); + ASSERT_EQ("dlopen failed: library \"" + lib_private_external_path + "\" is not accessible for the namespace \"private_isolated_shared\"", dlerror()); + + // load libnstest_root.so to shared namespace in order to check that everything is different + // except shared libnstest_dlopened.so + + handle2 = android_dlopen_ext(root_lib_isolated, RTLD_NOW, &extinfo); + + typedef const char* (*fn_t)(); + fn_t ns_get_local_string = reinterpret_cast(dlsym(handle1, "ns_get_local_string")); + ASSERT_TRUE(ns_get_local_string != nullptr) << dlerror(); + fn_t ns_get_local_string_shared = reinterpret_cast(dlsym(handle2, "ns_get_local_string")); + ASSERT_TRUE(ns_get_local_string_shared != nullptr) << dlerror(); + + ASSERT_STREQ("This string is local to root library", ns_get_local_string()); + ASSERT_STREQ("This string is local to root library", ns_get_local_string_shared()); + ASSERT_TRUE(ns_get_local_string() != ns_get_local_string_shared()); + + fn_t ns_get_private_extern_string = + reinterpret_cast(dlsym(handle1, "ns_get_private_extern_string")); + ASSERT_TRUE(ns_get_private_extern_string != nullptr) << dlerror(); + fn_t ns_get_private_extern_string_shared = + reinterpret_cast(dlsym(handle2, "ns_get_private_extern_string")); + ASSERT_TRUE(ns_get_private_extern_string_shared() != nullptr) << dlerror(); + + ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string()); + ASSERT_STREQ("This string is from private namespace", ns_get_private_extern_string_shared()); + ASSERT_TRUE(ns_get_private_extern_string() != ns_get_private_extern_string_shared()); + + fn_t ns_get_public_extern_string = + reinterpret_cast(dlsym(handle1, "ns_get_public_extern_string")); + ASSERT_TRUE(ns_get_public_extern_string != nullptr) << dlerror(); + fn_t ns_get_public_extern_string_shared = + reinterpret_cast(dlsym(handle2, "ns_get_public_extern_string")); + ASSERT_TRUE(ns_get_public_extern_string_shared != nullptr) << dlerror(); + + ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string()); + ASSERT_STREQ("This string is from public namespace", ns_get_public_extern_string_shared()); + ASSERT_TRUE(ns_get_public_extern_string() == ns_get_public_extern_string_shared()); + + fn_t ns_get_dlopened_string = reinterpret_cast(dlsym(handle1, "ns_get_dlopened_string")); + ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror(); + fn_t ns_get_dlopened_string_shared = reinterpret_cast(dlsym(handle2, "ns_get_dlopened_string")); + ASSERT_TRUE(ns_get_dlopened_string_shared != nullptr) << dlerror(); + const char** ns_dlopened_string = static_cast(dlsym(handle_dlopened, "g_private_dlopened_string")); + ASSERT_TRUE(ns_dlopened_string != nullptr) << dlerror(); + + ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string()); + ASSERT_STREQ("This string is from private namespace (dlopened library)", *ns_dlopened_string); + ASSERT_STREQ("This string is from private namespace (dlopened library)", ns_get_dlopened_string_shared()); + ASSERT_TRUE(ns_get_dlopened_string() != ns_get_dlopened_string_shared()); + ASSERT_TRUE(*ns_dlopened_string == ns_get_dlopened_string_shared()); + + dlclose(handle1); + dlclose(handle2); +} + 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; @@ -825,7 +958,7 @@ TEST(dlext, ns_anonymous) { android_namespace_t* ns = android_create_namespace( "private", nullptr, (lib_path + "/private_namespace_libs").c_str(), - false, nullptr); + ANDROID_NAMESPACE_TYPE_REGULAR, nullptr); ASSERT_TRUE(ns != nullptr) << dlerror();