Merge "linker: implement shared namespaces"

This commit is contained in:
Dimitry Ivanov 2015-12-21 23:10:49 +00:00 committed by Gerrit Code Review
commit be7c7fe218
7 changed files with 208 additions and 29 deletions

View File

@ -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

View File

@ -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;
}

View File

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

View File

@ -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<std::string> ld_library_paths;
std::vector<std::string> 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;
}

View File

@ -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

View File

@ -55,6 +55,7 @@
*********************************************************************/
#include "private/libc_logging.h"
#include <unistd.h>
__LIBC_HIDDEN__ extern int g_ld_debug_verbosity;

View File

@ -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<fn_t>(dlsym(handle1, "ns_get_local_string"));
ASSERT_TRUE(ns_get_local_string != nullptr) << dlerror();
fn_t ns_get_local_string_shared = reinterpret_cast<fn_t>(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<fn_t>(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<fn_t>(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<fn_t>(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<fn_t>(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<fn_t>(dlsym(handle1, "ns_get_dlopened_string"));
ASSERT_TRUE(ns_get_dlopened_string != nullptr) << dlerror();
fn_t ns_get_dlopened_string_shared = reinterpret_cast<fn_t>(dlsym(handle2, "ns_get_dlopened_string"));
ASSERT_TRUE(ns_get_dlopened_string_shared != nullptr) << dlerror();
const char** ns_dlopened_string = static_cast<const char**>(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();