From dedcf35bcc98e8b62fb6480022cd11ae671280ad Mon Sep 17 00:00:00 2001 From: Dimitry Ivanov Date: Mon, 27 Mar 2017 14:11:02 -0700 Subject: [PATCH] Fix lookup logic for linked namespaces When looking for already loaded libraries include linked namespaces to the search, but check if the library is accessible from the main namespace. Bug: http://b/36008422 Bug: http://b/35417197 Bug: http://b/34052337 Bug: http://b/36660652 Bug: https://issuetracker.google.com/36636090 Test: run bionic-unit-tests --gtest_filter=dl*:Dl* Change-Id: Ic7c1d48114da3ca5dc6512ef03f595dd17b6ed17 (cherry picked from commit d3e7d088453e089b3d625b0864ccdf3c74893f18) --- linker/linker.cpp | 101 ++++++++++++++++++++++++++++++++++++++------------- tests/dlext_test.cpp | 59 ++++++++++++++++++++++++++---- 2 files changed, 126 insertions(+), 34 deletions(-) diff --git a/linker/linker.cpp b/linker/linker.cpp index e2d61151f..1647db70b 100644 --- a/linker/linker.cpp +++ b/linker/linker.cpp @@ -1112,11 +1112,43 @@ static void for_each_dt_needed(const ElfReader& elf_reader, F action) { } } +static bool find_loaded_library_by_inode(android_namespace_t* ns, + const struct stat& file_stat, + off64_t file_offset, + bool search_linked_namespaces, + soinfo** candidate) { + + auto predicate = [&](soinfo* si) { + return si->get_st_dev() != 0 && + si->get_st_ino() != 0 && + si->get_st_dev() == file_stat.st_dev && + si->get_st_ino() == file_stat.st_ino && + si->get_file_offset() == file_offset; + }; + + *candidate = ns->soinfo_list().find_if(predicate); + + if (*candidate == nullptr && search_linked_namespaces) { + for (auto& link : ns->linked_namespaces()) { + android_namespace_t* linked_ns = link.linked_namespace(); + soinfo* si = linked_ns->soinfo_list().find_if(predicate); + + if (si != nullptr && link.is_accessible(si->get_soname())) { + *candidate = si; + return true; + } + } + } + + return *candidate != nullptr; +} + static bool load_library(android_namespace_t* ns, LoadTask* task, LoadTaskList* load_tasks, int rtld_flags, - const std::string& realpath) { + const std::string& realpath, + bool search_linked_namespaces) { off64_t file_offset = task->get_file_offset(); const char* name = task->get_name(); const android_dlextinfo* extinfo = task->get_extinfo(); @@ -1144,17 +1176,8 @@ static bool load_library(android_namespace_t* ns, // Check for symlink and other situations where // file can have different names, unless ANDROID_DLEXT_FORCE_LOAD is set if (extinfo == nullptr || (extinfo->flags & ANDROID_DLEXT_FORCE_LOAD) == 0) { - auto predicate = [&](soinfo* si) { - return si->get_st_dev() != 0 && - si->get_st_ino() != 0 && - si->get_st_dev() == file_stat.st_dev && - si->get_st_ino() == file_stat.st_ino && - si->get_file_offset() == file_offset; - }; - - soinfo* si = ns->soinfo_list().find_if(predicate); - - if (si != nullptr) { + soinfo* si = nullptr; + if (find_loaded_library_by_inode(ns, file_stat, file_offset, search_linked_namespaces, &si)) { TRACE("library \"%s\" is already loaded under different name/path \"%s\" - " "will return existing soinfo", name, si->get_realpath()); task->set_soinfo(si); @@ -1249,7 +1272,8 @@ static bool load_library(android_namespace_t* ns, LoadTask* task, ZipArchiveCache* zip_archive_cache, LoadTaskList* load_tasks, - int rtld_flags) { + int rtld_flags, + bool search_linked_namespaces) { const char* name = task->get_name(); soinfo* needed_by = task->get_needed_by(); const android_dlextinfo* extinfo = task->get_extinfo(); @@ -1270,7 +1294,7 @@ static bool load_library(android_namespace_t* ns, task->set_fd(extinfo->library_fd, false); task->set_file_offset(file_offset); - return load_library(ns, task, load_tasks, rtld_flags, realpath); + return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces); } // Open the file. @@ -1283,12 +1307,28 @@ static bool load_library(android_namespace_t* ns, task->set_fd(fd, true); task->set_file_offset(file_offset); - return load_library(ns, task, load_tasks, rtld_flags, realpath); + return load_library(ns, task, load_tasks, rtld_flags, realpath, search_linked_namespaces); +} + +static bool find_loaded_library_by_soname(android_namespace_t* ns, + const char* name, + soinfo** candidate) { + return !ns->soinfo_list().visit([&](soinfo* si) { + const char* soname = si->get_soname(); + if (soname != nullptr && (strcmp(name, soname) == 0)) { + *candidate = si; + return false; + } + + return true; + }); } // Returns true if library was found and false otherwise static bool find_loaded_library_by_soname(android_namespace_t* ns, - const char* name, soinfo** candidate) { + const char* name, + bool search_linked_namespaces, + soinfo** candidate) { *candidate = nullptr; // Ignore filename with path. @@ -1296,15 +1336,24 @@ static bool find_loaded_library_by_soname(android_namespace_t* ns, return false; } - return !ns->soinfo_list().visit([&](soinfo* si) { - const char* soname = si->get_soname(); - if (soname != nullptr && (strcmp(name, soname) == 0)) { - *candidate = si; - return false; + bool found = find_loaded_library_by_soname(ns, name, candidate); + + if (!found && search_linked_namespaces) { + // if a library was not found - look into linked namespaces + for (auto& link : ns->linked_namespaces()) { + if (!link.is_accessible(name)) { + continue; + } + + android_namespace_t* linked_ns = link.linked_namespace(); + + if (find_loaded_library_by_soname(linked_ns, name, candidate)) { + return true; + } } + } - return true; - }); + return found; } static bool find_library_in_linked_namespace(const android_namespace_link_t& namespace_link, @@ -1316,7 +1365,7 @@ static bool find_library_in_linked_namespace(const android_namespace_link_t& nam bool loaded = false; std::string soname; - if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) { + if (find_loaded_library_by_soname(ns, task->get_name(), false, &candidate)) { loaded = true; soname = candidate->get_soname(); } else { @@ -1367,7 +1416,7 @@ static bool find_library_internal(android_namespace_t* ns, bool search_linked_namespaces) { soinfo* candidate; - if (find_loaded_library_by_soname(ns, task->get_name(), &candidate)) { + if (find_loaded_library_by_soname(ns, task->get_name(), search_linked_namespaces, &candidate)) { task->set_soinfo(candidate); return true; } @@ -1377,7 +1426,7 @@ static bool find_library_internal(android_namespace_t* ns, TRACE("[ \"%s\" find_loaded_library_by_soname failed (*candidate=%s@%p). Trying harder...]", task->get_name(), candidate == nullptr ? "n/a" : candidate->get_realpath(), candidate); - if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags)) { + if (load_library(ns, task, zip_archive_cache, load_tasks, rtld_flags, search_linked_namespaces)) { return true; } diff --git a/tests/dlext_test.cpp b/tests/dlext_test.cpp index 808b708da..a0226a6f6 100644 --- a/tests/dlext_test.cpp +++ b/tests/dlext_test.cpp @@ -672,8 +672,59 @@ TEST(dlext, ns_smoke) { ASSERT_TRUE(handle != nullptr) << dlerror(); dlclose(handle); + // dlopen for a public library using an absolute path should work + // 1. For isolated namespaces android_dlextinfo extinfo; extinfo.flags = ANDROID_DLEXT_USE_NAMESPACE; + extinfo.library_namespace = ns2; + handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle != nullptr) << dlerror(); + ASSERT_TRUE(handle == handle_public); + + dlclose(handle); + + // 1.1 even if it wasn't loaded before + dlclose(handle_public); + + handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle_public == nullptr); + ASSERT_EQ(std::string("dlopen failed: library \"") + lib_public_path + + "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); + + handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW); + ASSERT_TRUE(handle == handle_public); + + dlclose(handle); + + // 2. And for regular namespaces (make sure it does not load second copy of the library) + extinfo.library_namespace = ns1; + handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle != nullptr) << dlerror(); + ASSERT_TRUE(handle == handle_public); + + dlclose(handle); + + // 2.1 Unless it was not loaded before - in which case it will load a duplicate. + // TODO(dimitry): This is broken. Maybe we need to deprecate non-isolated namespaces? + dlclose(handle_public); + + handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW | RTLD_NOLOAD); + ASSERT_TRUE(handle_public == nullptr); + ASSERT_EQ(std::string("dlopen failed: library \"") + lib_public_path + + "\" wasn't loaded and RTLD_NOLOAD prevented it", dlerror()); + + handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo); + ASSERT_TRUE(handle != nullptr) << dlerror(); + + handle_public = dlopen(lib_public_path.c_str(), RTLD_NOW); + + ASSERT_TRUE(handle != handle_public); + + dlclose(handle); + extinfo.library_namespace = ns1; void* handle1 = android_dlopen_ext(root_lib, RTLD_NOW, &extinfo); @@ -685,14 +736,6 @@ TEST(dlext, ns_smoke) { ASSERT_TRUE(handle1 != handle2); - // dlopen for a public library using an absolute path should work for isolated namespaces - extinfo.library_namespace = ns2; - handle = android_dlopen_ext(lib_public_path.c_str(), RTLD_NOW, &extinfo); - ASSERT_TRUE(handle != nullptr) << dlerror(); - ASSERT_TRUE(handle == handle_public); - - dlclose(handle); - typedef const char* (*fn_t)(); fn_t ns_get_local_string1 = reinterpret_cast(dlsym(handle1, "ns_get_local_string")); -- 2.11.0