From 284ae3559ed909613b189b98bdc3efab94373a30 Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Tue, 8 Dec 2015 10:47:13 -0800 Subject: [PATCH] Add permitted_when_isolated_path to linker namespaces The permitted_when_isolated_path is a way to white-list directories not present in search-path. It is ignored for not isolated namespaces. Bug: http://b/25853516 Bug: http://b/22548808 Change-Id: Ib1538037268eea69323ea49968a34a4a1d1938a5 --- libc/include/android/dlext.h | 14 +++++++++----- libdl/libdl.c | 3 ++- linker/dlfcn.cpp | 7 ++++--- linker/linker.cpp | 19 +++++++++++++++++-- linker/linker.h | 3 ++- linker/linker_utils.cpp | 15 ++++++++++++--- linker/linker_utils.h | 1 + linker/tests/linker_utils_test.cpp | 12 ++++++++++++ tests/dlext_test.cpp | 21 +++++++++++---------- 9 files changed, 70 insertions(+), 25 deletions(-) diff --git a/libc/include/android/dlext.h b/libc/include/android/dlext.h index ed9a3b997..7979c43d4 100644 --- a/libc/include/android/dlext.h +++ b/libc/include/android/dlext.h @@ -152,16 +152,20 @@ 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 resulted namespace requires all of the libraries - * to be on the search path; the search_path is ld_library_path: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. * - * If a library or any of its dependencies are outside of the search path and not - * part of the public namespace dlopen will fail. + * 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); + bool is_isolated, + const char* permitted_when_isolated_path); __END_DECLS diff --git a/libdl/libdl.c b/libdl/libdl.c index 3928ba2cd..af2f83e78 100644 --- a/libdl/libdl.c +++ b/libdl/libdl.c @@ -57,6 +57,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) { + bool isolated __unused, + const char* permitted_when_isolated_path __unused) { return 0; } diff --git a/linker/dlfcn.cpp b/linker/dlfcn.cpp index d07ec867b..fde9f5cf4 100644 --- a/linker/dlfcn.cpp +++ b/linker/dlfcn.cpp @@ -193,11 +193,12 @@ 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, bool is_isolated, + const char* permitted_when_isolated_path) { ScopedPthreadMutexLocker locker(&g_dl_mutex); - android_namespace_t* result = create_namespace(name, ld_library_path, - default_library_path, is_isolated); + android_namespace_t* result = create_namespace(name, ld_library_path, default_library_path, + is_isolated, 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 82d0d9eb6..3b67625b1 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -94,6 +94,10 @@ struct android_namespace_t { default_library_paths_ = library_paths; } + void set_permitted_paths(std::vector&& permitted_paths) { + permitted_paths_ = permitted_paths; + } + soinfo::soinfo_list_t& soinfo_list() { return soinfo_list_; } // For isolated namespaces - checks if the file is on the search path; @@ -105,6 +109,7 @@ struct android_namespace_t { bool is_isolated_; std::vector ld_library_paths_; std::vector default_library_paths_; + std::vector permitted_paths_; soinfo::soinfo_list_t soinfo_list_; DISALLOW_COPY_AND_ASSIGN(android_namespace_t); @@ -316,6 +321,12 @@ bool android_namespace_t::is_accessible(const std::string& file) { } } + for (const auto& dir : permitted_paths_) { + if (file_is_under_dir(file, dir)) { + return true; + } + } + return false; } @@ -2285,7 +2296,7 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_ // create anonymous namespace android_namespace_t* anon_ns = - create_namespace("(anonymous)", nullptr, anon_ns_library_path, false); + create_namespace("(anonymous)", nullptr, anon_ns_library_path, false, nullptr); if (anon_ns == nullptr) { g_public_namespace_initialized = false; @@ -2299,7 +2310,8 @@ bool init_namespaces(const char* public_ns_sonames, const char* anon_ns_library_ android_namespace_t* create_namespace(const char* name, const char* ld_library_path, const char* default_library_path, - bool is_isolated) { + bool is_isolated, + const char* permitted_when_isolated_path) { if (!g_public_namespace_initialized) { DL_ERR("cannot create namespace: public namespace is not initialized."); return nullptr; @@ -2308,15 +2320,18 @@ android_namespace_t* create_namespace(const char* name, ProtectedDataGuard guard; std::vector ld_library_paths; std::vector default_library_paths; + std::vector permitted_paths; parse_path(ld_library_path, ":", &ld_library_paths); parse_path(default_library_path, ":", &default_library_paths); + parse_path(permitted_when_isolated_path, ":", &permitted_paths); android_namespace_t* ns = new (g_namespace_allocator.alloc()) android_namespace_t(); ns->set_name(name); ns->set_isolated(is_isolated); 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); diff --git a/linker/linker.h b/linker/linker.h index b391fc3d6..49329c7b5 100644 --- a/linker/linker.h +++ b/linker/linker.h @@ -446,6 +446,7 @@ 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* default_library_path, bool is_isolated, + const char* permitted_when_isolated_path); #endif diff --git a/linker/linker_utils.cpp b/linker/linker_utils.cpp index db43d38f8..1b0c4e4c9 100644 --- a/linker/linker_utils.cpp +++ b/linker/linker_utils.cpp @@ -66,9 +66,18 @@ bool file_is_in_dir(const std::string& file, const std::string& dir) { const char* haystack = file.c_str(); size_t needle_len = strlen(needle); - return (strncmp(haystack, needle, needle_len) == 0 && - haystack[needle_len] == '/' && - strchr(haystack + needle_len + 1, '/') == nullptr); + return strncmp(haystack, needle, needle_len) == 0 && + haystack[needle_len] == '/' && + strchr(haystack + needle_len + 1, '/') == nullptr; +} + +bool file_is_under_dir(const std::string& file, const std::string& dir) { + const char* needle = dir.c_str(); + const char* haystack = file.c_str(); + size_t needle_len = strlen(needle); + + return strncmp(haystack, needle, needle_len) == 0 && + haystack[needle_len] == '/'; } const char* const kZipFileSeparator = "!/"; diff --git a/linker/linker_utils.h b/linker/linker_utils.h index 65ffbdc5f..987eabd9c 100644 --- a/linker/linker_utils.h +++ b/linker/linker_utils.h @@ -22,6 +22,7 @@ extern const char* const kZipFileSeparator; bool normalize_path(const char* path, std::string* normalized_path); bool file_is_in_dir(const std::string& file, const std::string& dir); +bool file_is_under_dir(const std::string& file, const std::string& dir); bool parse_zip_path(const char* input_path, std::string* zip_path, std::string* entry_path); off64_t page_start(off64_t offset); diff --git a/linker/tests/linker_utils_test.cpp b/linker/tests/linker_utils_test.cpp index 3be9b3f37..fd749fa40 100644 --- a/linker/tests/linker_utils_test.cpp +++ b/linker/tests/linker_utils_test.cpp @@ -54,6 +54,18 @@ TEST(linker_utils, file_is_in_dir_smoke) { ASSERT_FALSE(file_is_in_dir("/file", "/")); } +TEST(linker_utils, file_is_under_dir_smoke) { + ASSERT_TRUE(file_is_under_dir("/foo/bar/file", "/foo/bar")); + ASSERT_TRUE(file_is_under_dir("/foo/bar/file", "/foo")); + + ASSERT_FALSE(file_is_under_dir("/foo/bar/file", "/bar/foo")); + + ASSERT_TRUE(file_is_under_dir("/file", "")); + ASSERT_TRUE(file_is_under_dir("/foo/bar/file", "")); + ASSERT_FALSE(file_is_under_dir("/file", "/")); + ASSERT_FALSE(file_is_under_dir("/foo/bar/file", "/")); +} + TEST(linker_utils, parse_zip_path_smoke) { std::string zip_path; std::string entry_path; diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 97b52080f..5327e36b2 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -628,10 +628,10 @@ TEST(dlext, ns_smoke) { 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); + android_namespace_t* ns1 = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, nullptr); ASSERT_TRUE(ns1 != nullptr) << dlerror(); - android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true); + android_namespace_t* ns2 = android_create_namespace("private_isolated", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, nullptr); ASSERT_TRUE(ns2 != nullptr) << dlerror(); // This should not have affect search path for default namespace: @@ -732,13 +732,13 @@ 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); + android_namespace_t* ns_not_isolated = android_create_namespace("private", nullptr, (lib_path + "/private_namespace_libs").c_str(), false, 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); + android_namespace_t* ns_isolated = android_create_namespace("private_isolated1", nullptr, (lib_path + "/private_namespace_libs").c_str(), true, 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); + android_namespace_t* ns_isolated2 = android_create_namespace("private_isolated2", (lib_path + "/private_namespace_libs").c_str(), nullptr, true, lib_path.c_str()); ASSERT_TRUE(ns_isolated2 != nullptr) << dlerror(); ASSERT_TRUE(dlopen(root_lib, RTLD_NOW) == nullptr); @@ -772,14 +772,15 @@ TEST(dlext, ns_isolated) { extinfo.library_namespace = ns_isolated2; + // this should work because isolation_path for private_isolated2 includes lib_path 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()); + ASSERT_TRUE(handle2 != nullptr) << dlerror(); + dlclose(handle2); // 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_isolated2\"", dlerror()); + ASSERT_TRUE(handle2 != nullptr) << dlerror(); + dlclose(handle2); typedef const char* (*fn_t)(); fn_t ns_get_local_string = reinterpret_cast(dlsym(handle1, "ns_get_local_string")); @@ -824,7 +825,7 @@ TEST(dlext, ns_anonymous) { android_namespace_t* ns = android_create_namespace( "private", nullptr, (lib_path + "/private_namespace_libs").c_str(), - false); + false, nullptr); ASSERT_TRUE(ns != nullptr) << dlerror();