From 34a4acd801fbd401fcc3c62928effac7b161b1d8 Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Sat, 20 May 2017 01:01:24 +0900 Subject: [PATCH] linker: the global group is added to all built-in namespaces With ld.config.txt, we now have multiple built-in namespaces other than the default namespace. Libs (and their dependents) listed in LD_PRELOAD must be visible to those additional namespaces as well. This also adds a debugging only feature: path to the linker config file can be customized via LD_CONFIG_FILE environment variable. This works only for debuggable builds. Bug: 38114603 Bug: 62815515 Test: 1. ./external/compiler-rt/lib/asan/scripts/asan_device_setup --lib prebuilts/clang/host/linux-x86/clang-stable/lib64/clang/5.0/lib/linux 2. enable talkback shortcut 3. in the home screen, hold vol-up/down together 4. device does not reboots and talkback shortcut is toggled Test: bionic-unit-tests and linker-unit-tests successful Merged-In: I9a03591053f4a9caea82f0dcb23e7a3d324bb9bd Change-Id: I9a03591053f4a9caea82f0dcb23e7a3d324bb9bd (cherry picked from commit 02586a2a34e6acfccf359b94db840f422b6c0231) --- linker/Android.bp | 6 + linker/linker.cpp | 218 +++++++++++++++++------------ linker/linker_main.cpp | 16 ++- linker/linker_main.h | 11 +- linker/linker_namespaces.cpp | 40 ++++++ linker/linker_namespaces.h | 3 + linker/linker_phdr.cpp | 9 +- tests/Android.bp | 14 +- tests/dl_test.cpp | 131 ++++++++++++++++- tests/libs/Android.bp | 56 ++++++++ tests/libs/ld_config_test_helper.cpp | 26 ++++ tests/libs/ld_config_test_helper_lib1.cpp | 4 + tests/libs/ld_config_test_helper_lib2.cpp | 3 + tests/libs/ld_config_test_helper_lib3.cpp | 3 + tests/libs/ld_preload_test_helper.cpp | 26 ++++ tests/libs/ld_preload_test_helper_lib1.cpp | 3 + tests/libs/ld_preload_test_helper_lib2.cpp | 3 + 17 files changed, 472 insertions(+), 100 deletions(-) create mode 100644 tests/libs/ld_config_test_helper.cpp create mode 100644 tests/libs/ld_config_test_helper_lib1.cpp create mode 100644 tests/libs/ld_config_test_helper_lib2.cpp create mode 100644 tests/libs/ld_config_test_helper_lib3.cpp create mode 100644 tests/libs/ld_preload_test_helper.cpp create mode 100644 tests/libs/ld_preload_test_helper_lib1.cpp create mode 100644 tests/libs/ld_preload_test_helper_lib2.cpp diff --git a/linker/Android.bp b/linker/Android.bp index efd91ac88..fda7eb5fa 100644 --- a/linker/Android.bp +++ b/linker/Android.bp @@ -101,6 +101,12 @@ cc_binary { "-Werror", ], + product_variables: { + debuggable: { + cppflags: ["-DUSE_LD_CONFIG_FILE"], + }, + }, + cppflags: ["-Wold-style-cast"], // we are going to link libc++_static manually because diff --git a/linker/linker.cpp b/linker/linker.cpp index b05ab9124..4397551d2 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -558,9 +558,10 @@ class LoadTask { static LoadTask* create(const char* name, soinfo* needed_by, + android_namespace_t* start_from, std::unordered_map* readers_map) { LoadTask* ptr = TypeBasedAllocator::alloc(); - return new (ptr) LoadTask(name, needed_by, readers_map); + return new (ptr) LoadTask(name, needed_by, start_from, readers_map); } const char* get_name() const { @@ -612,6 +613,11 @@ class LoadTask { is_dt_needed_ = is_dt_needed; } + // returns the namespace from where we need to start loading this. + const android_namespace_t* get_start_from() const { + return start_from_; + } + const ElfReader& get_elf_reader() const { CHECK(si_ != nullptr); return (*elf_readers_map_)[si_]; @@ -650,10 +656,11 @@ class LoadTask { private: LoadTask(const char* name, soinfo* needed_by, + android_namespace_t* start_from, std::unordered_map* readers_map) : name_(name), needed_by_(needed_by), si_(nullptr), fd_(-1), close_fd_(false), file_offset_(0), elf_readers_map_(readers_map), - is_dt_needed_(false) {} + is_dt_needed_(false), start_from_(start_from) {} ~LoadTask() { if (fd_ != -1 && close_fd_) { @@ -672,6 +679,7 @@ class LoadTask { // TODO(dimitry): needed by workaround for http://b/26394120 (the grey-list) bool is_dt_needed_; // END OF WORKAROUND + const android_namespace_t* const start_from_; DISALLOW_IMPLICIT_CONSTRUCTORS(LoadTask); }; @@ -1041,7 +1049,7 @@ static int open_library(android_namespace_t* ns, ZipArchiveCache* zip_archive_cache, const char* name, soinfo *needed_by, off64_t* file_offset, std::string* realpath) { - TRACE("[ opening %s ]", name); + TRACE("[ opening %s at namespace %s]", name, ns->get_name()); // If the name contains a slash, we should attempt to open it directly and not search the paths. if (strchr(name, '/') != nullptr) { @@ -1294,7 +1302,7 @@ static bool load_library(android_namespace_t* ns, } for_each_dt_needed(task->get_elf_reader(), [&](const char* name) { - load_tasks->push_back(LoadTask::create(name, si, task->get_readers_map())); + load_tasks->push_back(LoadTask::create(name, si, ns, task->get_readers_map())); }); return true; @@ -1389,8 +1397,7 @@ static bool find_loaded_library_by_soname(android_namespace_t* ns, } static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link, - LoadTask* task, - int rtld_flags) { + LoadTask* task) { android_namespace_t* ns = namespace_link.linked_namespace(); soinfo* candidate; @@ -1415,29 +1422,10 @@ static bool find_library_in_linked_namespace(const android_namespace_link_t& nam return true; } - // try to load the library - once namespace boundary is crossed - // we need to load a library within separate load_group - // to avoid using symbols from foreign namespace while. - // - // All symbols during relocation should be resolved within a - // namespace to preserve library locality to a namespace. - const char* name = task->get_name(); - if (find_libraries(ns, - task->get_needed_by(), - &name, - 1, - &candidate, - nullptr /* ld_preloads */, - 0 /* ld_preload_count*/, - rtld_flags, - nullptr /* extinfo*/, - false /* add_as_children */, - false /* search_linked_namespaces */)) { - task->set_soinfo(candidate); - return true; - } - - return false; + // returning true with empty soinfo means that the library is okay to be + // loaded in the namespace buy has not yet been loaded there before. + task->set_soinfo(nullptr); + return true; } static bool find_library_internal(android_namespace_t* ns, @@ -1466,9 +1454,24 @@ static bool find_library_internal(android_namespace_t* ns, // if a library was not found - look into linked namespaces for (auto& linked_namespace : ns->linked_namespaces()) { if (find_library_in_linked_namespace(linked_namespace, - task, - rtld_flags)) { - return true; + task)) { + if (task->get_soinfo() == nullptr) { + // try to load the library - once namespace boundary is crossed + // we need to load a library within separate load_group + // to avoid using symbols from foreign namespace while. + // + // However, actual linking is deferred until when the global group + // is fully identified and is applied to all namespaces. + // Otherwise, the libs in the linked namespace won't get symbols from + // the global group. + if (load_library(linked_namespace.linked_namespace(), task, zip_archive_cache, load_tasks, rtld_flags, false)) { + return true; + } + // lib was not found in the namespace. Try next linked namespace. + } else { + // lib is already loaded + return true; + } } } } @@ -1479,44 +1482,6 @@ static bool find_library_internal(android_namespace_t* ns, static void soinfo_unload(soinfo* si); static void soinfo_unload(soinfo* soinfos[], size_t count); -// TODO: this is slightly unusual way to construct -// the global group for relocation. Not every RTLD_GLOBAL -// library is included in this group for backwards-compatibility -// reasons. -// -// This group consists of the main executable, LD_PRELOADs -// and libraries with the DF_1_GLOBAL flag set. -static soinfo_list_t make_global_group(android_namespace_t* ns) { - soinfo_list_t global_group; - ns->soinfo_list().for_each([&](soinfo* si) { - if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { - global_group.push_back(si); - } - }); - - 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_list_t get_shared_group(android_namespace_t* ns) { - if (ns == &g_default_namespace) { - return make_global_group(ns); - } - - 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; @@ -1539,19 +1504,17 @@ bool find_libraries(android_namespace_t* ns, int rtld_flags, const android_dlextinfo* extinfo, bool add_as_children, - bool search_linked_namespaces) { + bool search_linked_namespaces, + std::unordered_map& readers_map, + std::vector* namespaces) { // Step 0: prepare. LoadTaskList load_tasks; - std::unordered_map readers_map; for (size_t i = 0; i < library_names_count; ++i) { const char* name = library_names[i]; - load_tasks.push_back(LoadTask::create(name, start_with, &readers_map)); + load_tasks.push_back(LoadTask::create(name, start_with, ns, &readers_map)); } - // Construct global_group. - soinfo_list_t global_group = make_global_group(ns); - // If soinfos array is null allocate one on stack. // The array is needed in case of failure; for example // when library_names[] = {libone.so, libtwo.so} and libone.so @@ -1591,7 +1554,12 @@ bool find_libraries(android_namespace_t* ns, task->set_extinfo(is_dt_needed ? nullptr : extinfo); task->set_dt_needed(is_dt_needed); - if (!find_library_internal(ns, + // try to find the load. + // Note: start from the namespace that is stored in the LoadTask. This namespace + // is different from the current namespace when the LoadTask is for a transitive + // dependency and the lib that created the LoadTask is not found in the + // current namespace but in one of the linked namespace. + if (!find_library_internal(const_cast(task->get_start_from()), task, &zip_archive_cache, &load_tasks, @@ -1650,18 +1618,61 @@ bool find_libraries(android_namespace_t* ns, } } - // Step 4: Add LD_PRELOADed libraries to the global group for - // future runs. There is no need to explicitly add them to - // the global group for this run because they are going to - // appear in the local group in the correct order. + // Step 4: Construct the global group. Note: DF_1_GLOBAL bit of a library is + // determined at step 3. + + // Step 4-1: DF_1_GLOBAL bit is force set for LD_PRELOADed libs because they + // must be added to the global group if (ld_preloads != nullptr) { for (auto&& si : *ld_preloads) { si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); } } + // Step 4-2: Gather all DF_1_GLOBAL libs which were newly loaded during this + // run. These will be the new member of the global group + soinfo_list_t new_global_group_members; + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + if (!si->is_linked() && (si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { + new_global_group_members.push_back(si); + } + } + + // Step 4-3: Add the new global group members to all the linked namespaces + for (auto si : new_global_group_members) { + for (auto linked_ns : *namespaces) { + if (si->get_primary_namespace() != linked_ns) { + linked_ns->add_soinfo(si); + si->add_secondary_namespace(linked_ns); + } + } + } - // Step 5: link libraries. + // Step 5: link libraries that are not destined to this namespace. + // Do this by recursively calling find_libraries on the namespace where the lib + // was found during Step 1. + for (auto&& task : load_tasks) { + soinfo* si = task->get_soinfo(); + if (si->get_primary_namespace() != ns) { + const char* name = task->get_name(); + if (find_libraries(si->get_primary_namespace(), task->get_needed_by(), &name, 1, + nullptr /* soinfos */, nullptr /* ld_preloads */, 0 /* ld_preload_count */, + rtld_flags, nullptr /* extinfo */, false /* add_as_children */, + false /* search_linked_namespaces */, readers_map, namespaces)) { + // If this lib is directly needed by one of the libs in this namespace, + // then increment the count + soinfo* needed_by = task->get_needed_by(); + if (needed_by != nullptr && needed_by->get_primary_namespace() == ns && si->is_linked()) { + si->increment_ref_count(); + } + } else { + return false; + } + } + } + + // Step 6: link libraries in this namespace soinfo_list_t local_group; walk_dependencies_tree( (start_with != nullptr && add_as_children) ? &start_with : soinfos, @@ -1675,6 +1686,7 @@ bool find_libraries(android_namespace_t* ns, } }); + soinfo_list_t global_group = ns->get_global_group(); bool linked = local_group.visit([&](soinfo* si) { if (!si->is_linked()) { if (!si->link_image(global_group, local_group, extinfo) || @@ -1705,6 +1717,9 @@ static soinfo* find_library(android_namespace_t* ns, soinfo* needed_by) { soinfo* si; + // readers_map is shared across recursive calls to find_libraries. + // However, the map is not shared across different threads. + std::unordered_map readers_map; if (name == nullptr) { si = solist_get_somain(); } else if (!find_libraries(ns, @@ -1717,7 +1732,8 @@ static soinfo* find_library(android_namespace_t* ns, rtld_flags, extinfo, false /* add_as_children */, - true /* search_linked_namespaces */)) { + true /* search_linked_namespaces */, + readers_map)) { return nullptr; } @@ -2235,7 +2251,7 @@ android_namespace_t* create_namespace(const void* caller_addr, } } else { // If not shared - copy only the shared group - add_soinfos_to_namespace(get_shared_group(parent_namespace), ns); + add_soinfos_to_namespace(parent_namespace->get_shared_group(), ns); } ns->set_ld_library_paths(std::move(ld_library_paths)); @@ -3440,7 +3456,7 @@ bool soinfo::protect_relro() { return true; } -static void init_default_namespace_no_config(bool is_asan) { +static std::vector init_default_namespace_no_config(bool is_asan) { g_default_namespace.set_isolated(false); auto default_ld_paths = is_asan ? kAsanDefaultLdPaths : kDefaultLdPaths; @@ -3455,9 +3471,13 @@ static void init_default_namespace_no_config(bool is_asan) { } g_default_namespace.set_default_library_paths(std::move(ld_default_paths)); + + std::vector namespaces; + namespaces.push_back(&g_default_namespace); + return namespaces; } -void init_default_namespace(const char* executable_path) { +std::vector init_default_namespaces(const char* executable_path) { g_default_namespace.set_name("(default)"); soinfo* somain = solist_get_somain(); @@ -3474,14 +3494,24 @@ void init_default_namespace(const char* executable_path) { std::string error_msg; - if (!Config::read_binary_config(kLdConfigFilePath, + const char* config_file = kLdConfigFilePath; +#ifdef USE_LD_CONFIG_FILE + // This is a debugging/testing only feature. Must not be available on + // production builds. + const char* ld_config_file = getenv("LD_CONFIG_FILE"); + if (ld_config_file != nullptr && file_exists(ld_config_file)) { + config_file = ld_config_file; + } +#endif + + if (!Config::read_binary_config(config_file, executable_path, g_is_asan, &config, &error_msg)) { if (!error_msg.empty()) { DL_WARN("error reading config file \"%s\" for \"%s\" (will use default configuration): %s", - kLdConfigFilePath, + config_file, executable_path, error_msg.c_str()); } @@ -3489,8 +3519,7 @@ void init_default_namespace(const char* executable_path) { } if (config == nullptr) { - init_default_namespace_no_config(g_is_asan); - return; + return init_default_namespace_no_config(g_is_asan); } const auto& namespace_configs = config->namespace_configs(); @@ -3541,10 +3570,17 @@ void init_default_namespace(const char* executable_path) { soinfo* ld_android_so = solist_get_head(); for (auto it : namespaces) { it.second->add_soinfo(ld_android_so); - // TODO (dimitry): somain and ld_preloads should probably be added to all of these namespaces too? + // somain and ld_preloads are added to these namespaces after LD_PRELOAD libs are linked } set_application_target_sdk_version(config->target_sdk_version()); + + std::vector created_namespaces; + created_namespaces.reserve(namespaces.size()); + for (auto kv : namespaces) { + created_namespaces.push_back(kv.second); + } + return created_namespaces; } // This function finds a namespace exported in ld.config.txt by its name. diff --git a/linker/linker_main.cpp b/linker/linker_main.cpp index 3f7795bc2..0f691c36d 100644 --- a/linker/linker_main.cpp +++ b/linker/linker_main.cpp @@ -337,7 +337,7 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) { somain = si; - init_default_namespace(executable_path); + std::vector namespaces = init_default_namespaces(executable_path); if (!si->prelink_image()) { async_safe_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); @@ -345,6 +345,13 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) { // add somain to global group si->set_dt_flags_1(si->get_dt_flags_1() | DF_1_GLOBAL); + // ... and add it to all other linked namespaces + for (auto linked_ns : namespaces) { + if (linked_ns != &g_default_namespace) { + linked_ns->add_soinfo(somain); + somain->add_secondary_namespace(linked_ns); + } + } // Load ld_preloads and dependencies. std::vector needed_library_name_list; @@ -362,6 +369,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) { const char** needed_library_names = &needed_library_name_list[0]; size_t needed_libraries_count = needed_library_name_list.size(); + // readers_map is shared across recursive calls to find_libraries so that we + // don't need to re-load elf headers. + std::unordered_map readers_map; if (needed_libraries_count > 0 && !find_libraries(&g_default_namespace, si, @@ -373,7 +383,9 @@ static ElfW(Addr) __linker_init_post_relocation(KernelArgumentBlock& args) { RTLD_GLOBAL, nullptr, true /* add_as_children */, - true /* search_linked_namespaces */)) { + true /* search_linked_namespaces */, + readers_map, + &namespaces)) { async_safe_fatal("CANNOT LINK EXECUTABLE \"%s\": %s", g_argv[0], linker_get_error_buffer()); } else if (needed_libraries_count == 0) { if (!si->link_image(g_empty_list, soinfo_list_t::make_list(si), nullptr)) { diff --git a/linker/linker_main.h b/linker/linker_main.h index 8f3f07ce5..2cf30c292 100644 --- a/linker/linker_main.h +++ b/linker/linker_main.h @@ -31,6 +31,9 @@ #include +#include +#include + #include "linker_namespaces.h" #include "linker_soinfo.h" @@ -44,7 +47,9 @@ class ProtectedDataGuard { static size_t ref_count_; }; -void init_default_namespace(const char* executable_path); +class ElfReader; + +std::vector init_default_namespaces(const char* executable_path); soinfo* soinfo_alloc(android_namespace_t* ns, const char* name, struct stat* file_stat, off64_t file_offset, uint32_t rtld_flags); @@ -59,7 +64,9 @@ bool find_libraries(android_namespace_t* ns, int rtld_flags, const android_dlextinfo* extinfo, bool add_as_children, - bool search_linked_namespaces); + bool search_linked_namespaces, + std::unordered_map& readers_map, + std::vector* namespaces = nullptr); void solist_add_soinfo(soinfo* si); bool solist_remove_soinfo(soinfo* si); diff --git a/linker/linker_namespaces.cpp b/linker/linker_namespaces.cpp index 3c86f9984..9fdf0b51c 100644 --- a/linker/linker_namespaces.cpp +++ b/linker/linker_namespaces.cpp @@ -31,6 +31,8 @@ #include "linker_soinfo.h" #include "linker_utils.h" +#include + bool android_namespace_t::is_accessible(const std::string& file) { if (!is_isolated_) { return true; @@ -86,3 +88,41 @@ bool android_namespace_t::is_accessible(soinfo* s) { return !is_accessible_ftor(si); }); } + +// TODO: this is slightly unusual way to construct +// the global group for relocation. Not every RTLD_GLOBAL +// library is included in this group for backwards-compatibility +// reasons. +// +// This group consists of the main executable, LD_PRELOADs +// and libraries with the DF_1_GLOBAL flag set. +soinfo_list_t android_namespace_t::get_global_group() { + soinfo_list_t global_group; + soinfo_list().for_each([&](soinfo* si) { + if ((si->get_dt_flags_1() & DF_1_GLOBAL) != 0) { + global_group.push_back(si); + } + }); + + 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 get_global_group). For all others this is a group +// of RTLD_GLOBAL libraries (which includes the global group from +// the default namespace). +soinfo_list_t android_namespace_t::get_shared_group() { + if (this == &g_default_namespace) { + return get_global_group(); + } + + soinfo_list_t shared_group; + soinfo_list().for_each([&](soinfo* si) { + if ((si->get_rtld_flags() & RTLD_GLOBAL) != 0) { + shared_group.push_back(si); + } + }); + + return shared_group; +} diff --git a/linker/linker_namespaces.h b/linker/linker_namespaces.h index 1099b6b14..16906d63f 100644 --- a/linker/linker_namespaces.h +++ b/linker/linker_namespaces.h @@ -136,6 +136,9 @@ struct android_namespace_t { // or one of it's parent soinfos belongs to this namespace. bool is_accessible(soinfo* si); + soinfo_list_t get_global_group(); + soinfo_list_t get_shared_group(); + private: const char* name_; bool is_isolated_; diff --git a/linker/linker_phdr.cpp b/linker/linker_phdr.cpp index 42c29c8c2..a9873c483 100644 --- a/linker/linker_phdr.cpp +++ b/linker/linker_phdr.cpp @@ -147,8 +147,9 @@ ElfReader::ElfReader() } bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file_size) { - CHECK(!did_read_); - CHECK(!did_load_); + if (did_read_) { + return true; + } name_ = name; fd_ = fd; file_offset_ = file_offset; @@ -167,7 +168,9 @@ bool ElfReader::Read(const char* name, int fd, off64_t file_offset, off64_t file bool ElfReader::Load(const android_dlextinfo* extinfo) { CHECK(did_read_); - CHECK(!did_load_); + if (did_load_) { + return true; + } if (ReserveAddressSpace(extinfo) && LoadSegments() && FindPhdr()) { diff --git a/tests/Android.bp b/tests/Android.bp index d4027b3da..e8fa5bdf9 100644 --- a/tests/Android.bp +++ b/tests/Android.bp @@ -298,7 +298,13 @@ cc_test_library { "libLLVMSupport", ], } - } + }, + + product_variables: { + debuggable: { + cppflags: ["-DUSE_LD_CONFIG_FILE"], + }, + }, } // ----------------------------------------------------------------------------- @@ -599,6 +605,12 @@ cc_test_host { sanitize: { never: false, }, + + product_variables: { + debuggable: { + cppflags: ["-DUSE_LD_CONFIG_FILE"], + }, + }, } subdirs = ["libs"] diff --git a/tests/dl_test.cpp b/tests/dl_test.cpp index aa8bd5740..857640ad6 100644 --- a/tests/dl_test.cpp +++ b/tests/dl_test.cpp @@ -23,6 +23,8 @@ #include #include +#include +#include #include "gtest_globals.h" #include "utils.h" @@ -109,4 +111,131 @@ TEST(dl, xfail_preinit_getauxval) { #endif } -// TODO: Add tests for LD_PRELOADs + +TEST(dl, exec_without_ld_preload) { +#if defined(__BIONIC__) + std::string helper = get_testlib_root() + + "/ld_preload_test_helper/ld_preload_test_helper"; + chmod(helper.c_str(), 0755); + ExecTestHelper eth; + eth.SetArgs({ helper.c_str(), nullptr }); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345"); +#endif +} + +TEST(dl, exec_with_ld_preload) { +#if defined(__BIONIC__) + std::string helper = get_testlib_root() + + "/ld_preload_test_helper/ld_preload_test_helper"; + std::string env = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_preload_test_helper_lib2.so"; + chmod(helper.c_str(), 0755); + ExecTestHelper eth; + eth.SetArgs({ helper.c_str(), nullptr }); + eth.SetEnv({ env.c_str(), nullptr }); + // ld_preload_test_helper calls get_value_from_lib() and returns the value. + // The symbol is defined by two libs: ld_preload_test_helper_lib.so and + // ld_preloaded_lib.so. The former is DT_NEEDED and the latter is LD_PRELOADED + // via this execution. The main executable is linked to the LD_PRELOADED lib + // and the value given from the lib is returned. + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321"); +#endif +} + + +// ld_config_test_helper must fail because it is depending on a lib which is not +// in the search path +// +// Call sequence is... +// _helper -- (get_value_from_lib()) --> +// _lib1.so -- (get_value_from_another_lib()) --> +// _lib2.so (returns 12345) +// The two libs are in ns2/ subdir. +TEST(dl, exec_without_ld_config_file) { +#if defined(__BIONIC__) + std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n"; + std::string helper = get_testlib_root() + + "/ld_config_test_helper/ld_config_test_helper"; + chmod(helper.c_str(), 0755); + ExecTestHelper eth; + eth.SetArgs({ helper.c_str(), nullptr }); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str()); +#endif +} + +#if defined(__BIONIC__) +static void create_ld_config_file(std::string& config_file) { + std::ofstream fout(config_file.c_str(), std::ios::out); + fout << "dir.test = " << get_testlib_root() << "/ld_config_test_helper/" << std::endl + << "[test]" << std::endl + << "additional.namespaces = ns2" << std::endl + << "namespace.default.search.paths = " << get_testlib_root() << std::endl + << "namespace.default.links = ns2" << std::endl + << "namespace.default.link.ns2.shared_libs = libc.so:libm.so:libdl.so:ld_config_test_helper_lib1.so" << std::endl + << "namespace.ns2.search.paths = /system/${LIB}:" << get_testlib_root() << "/ns2" << std::endl; + fout.close(); +} +#endif + +#ifdef USE_LD_CONFIG_FILE + +// _lib1.so and _lib2.so are now searchable by having another namespace 'ns2' +// whose search paths include the 'ns2/' subdir. +TEST(dl, exec_with_ld_config_file) { +#if defined(__BIONIC__) + std::string helper = get_testlib_root() + + "/ld_config_test_helper/ld_config_test_helper"; + std::string config_file = get_testlib_root() + "/ld.config.txt"; + create_ld_config_file(config_file); + std::string env = std::string("LD_CONFIG_FILE=") + config_file; + chmod(helper.c_str(), 0755); + ExecTestHelper eth; + eth.SetArgs({ helper.c_str(), nullptr }); + eth.SetEnv({ env.c_str(), nullptr }); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "12345"); +#endif +} + +// _lib3.so has same symbol as lib2.so but returns 54321. _lib3.so is +// LD_PRELOADed. This test is to ensure LD_PRELOADed libs are available to +// additional namespaces other than the default namespace. +TEST(dl, exec_with_ld_config_file_with_ld_preload) { +#if defined(__BIONIC__) + std::string helper = get_testlib_root() + + "/ld_config_test_helper/ld_config_test_helper"; + std::string config_file = get_testlib_root() + "/ld.config.txt"; + create_ld_config_file(config_file); + std::string env = std::string("LD_CONFIG_FILE=") + config_file; + std::string env2 = std::string("LD_PRELOAD=") + get_testlib_root() + "/ld_config_test_helper_lib3.so"; + chmod(helper.c_str(), 0755); + ExecTestHelper eth; + eth.SetArgs({ helper.c_str(), nullptr }); + eth.SetEnv({ env.c_str(), env2.c_str(), nullptr }); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, 0, "54321"); +#endif +} + +#endif // USE_LD_CONFIG_FILE + +// ensures that LD_CONFIG_FILE env var does not work for production builds. +// The test input is the same as exec_with_ld_config_file, but it must fail in +// this case. +TEST(dl, disable_ld_config_file) { +#if defined(__BIONIC__) + if (getuid() == 0) { + // when executed from the shell (e.g. not as part of CTS), skip the test. + // This test is only for CTS. + return; + } + std::string error_message = "CANNOT LINK EXECUTABLE \"" + get_testlib_root() + "/ld_config_test_helper/ld_config_test_helper\": library \"ld_config_test_helper_lib1.so\" not found\n"; + std::string helper = get_testlib_root() + + "/ld_config_test_helper/ld_config_test_helper"; + std::string config_file = get_testlib_root() + "/ld.config.txt"; + create_ld_config_file(config_file); + std::string env = std::string("LD_CONFIG_FILE=") + config_file; + chmod(helper.c_str(), 0755); + ExecTestHelper eth; + eth.SetArgs({ helper.c_str(), nullptr }); + eth.SetEnv({ env.c_str(), nullptr }); + eth.Run([&]() { execve(helper.c_str(), eth.GetArgs(), eth.GetEnv()); }, -6, error_message.c_str()); +#endif +} diff --git a/tests/libs/Android.bp b/tests/libs/Android.bp index 973a8d2f3..c1600cc24 100644 --- a/tests/libs/Android.bp +++ b/tests/libs/Android.bp @@ -617,3 +617,59 @@ cc_test { defaults: ["bionic_testlib_defaults"], srcs: ["preinit_syscall_test_helper.cpp"], } + +cc_test { + name: "ld_preload_test_helper", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_preload_test_helper.cpp"], + shared_libs: ["ld_preload_test_helper_lib1"], + ldflags: ["-Wl,--rpath,${ORIGIN}/.."], +} + +cc_test_library { + name: "ld_preload_test_helper_lib1", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_preload_test_helper_lib1.cpp"], +} + +cc_test_library { + name: "ld_preload_test_helper_lib2", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_preload_test_helper_lib2.cpp"], +} + +cc_test { + name: "ld_config_test_helper", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_config_test_helper.cpp"], + shared_libs: ["ld_config_test_helper_lib1"], + ldflags: ["-Wl,--rpath,${ORIGIN}/.."], +} + +cc_test_library { + name: "ld_config_test_helper_lib1", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_config_test_helper_lib1.cpp"], + shared_libs: ["ld_config_test_helper_lib2"], + relative_install_path: "/ns2", +} + +cc_test_library { + name: "ld_config_test_helper_lib2", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_config_test_helper_lib2.cpp"], + relative_install_path: "/ns2", +} + +cc_test_library { + name: "ld_config_test_helper_lib3", + host_supported: false, + defaults: ["bionic_testlib_defaults"], + srcs: ["ld_config_test_helper_lib3.cpp"], +} diff --git a/tests/libs/ld_config_test_helper.cpp b/tests/libs/ld_config_test_helper.cpp new file mode 100644 index 000000000..592e8c0b1 --- /dev/null +++ b/tests/libs/ld_config_test_helper.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +extern int get_value_from_lib(); + +int main() { + printf("%d", get_value_from_lib()); + return 0; +} diff --git a/tests/libs/ld_config_test_helper_lib1.cpp b/tests/libs/ld_config_test_helper_lib1.cpp new file mode 100644 index 000000000..fc5401a26 --- /dev/null +++ b/tests/libs/ld_config_test_helper_lib1.cpp @@ -0,0 +1,4 @@ +extern int get_value_from_another_lib(); +int get_value_from_lib() { + return get_value_from_another_lib(); +} diff --git a/tests/libs/ld_config_test_helper_lib2.cpp b/tests/libs/ld_config_test_helper_lib2.cpp new file mode 100644 index 000000000..a620a6cf5 --- /dev/null +++ b/tests/libs/ld_config_test_helper_lib2.cpp @@ -0,0 +1,3 @@ +int get_value_from_another_lib() { + return 12345; +} diff --git a/tests/libs/ld_config_test_helper_lib3.cpp b/tests/libs/ld_config_test_helper_lib3.cpp new file mode 100644 index 000000000..93d1cd81d --- /dev/null +++ b/tests/libs/ld_config_test_helper_lib3.cpp @@ -0,0 +1,3 @@ +int get_value_from_another_lib() { + return 54321; +} diff --git a/tests/libs/ld_preload_test_helper.cpp b/tests/libs/ld_preload_test_helper.cpp new file mode 100644 index 000000000..592e8c0b1 --- /dev/null +++ b/tests/libs/ld_preload_test_helper.cpp @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +extern int get_value_from_lib(); + +int main() { + printf("%d", get_value_from_lib()); + return 0; +} diff --git a/tests/libs/ld_preload_test_helper_lib1.cpp b/tests/libs/ld_preload_test_helper_lib1.cpp new file mode 100644 index 000000000..74e89db04 --- /dev/null +++ b/tests/libs/ld_preload_test_helper_lib1.cpp @@ -0,0 +1,3 @@ +int get_value_from_lib() { + return 12345; +} diff --git a/tests/libs/ld_preload_test_helper_lib2.cpp b/tests/libs/ld_preload_test_helper_lib2.cpp new file mode 100644 index 000000000..92398913b --- /dev/null +++ b/tests/libs/ld_preload_test_helper_lib2.cpp @@ -0,0 +1,3 @@ +int get_value_from_lib() { + return 54321; +} -- 2.11.0