1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 #define LOG_TAG "apex_shared_libraries_test" 17 18 #include <android-base/logging.h> 19 #include <android-base/properties.h> 20 #include <android-base/scopeguard.h> 21 #include <android-base/strings.h> 22 #include <android/dlext.h> 23 #include <dlfcn.h> 24 #include <fstab/fstab.h> 25 #include <gtest/gtest.h> 26 #include <link.h> 27 28 #include <filesystem> 29 #include <fstream> 30 #include <regex> 31 #include <sstream> 32 #include <string> 33 34 using android::base::GetBoolProperty; 35 using android::base::Split; 36 using android::base::StartsWith; 37 using android::fs_mgr::Fstab; 38 using android::fs_mgr::ReadFstabFromFile; 39 40 namespace fs = std::filesystem; 41 42 // No header available for these symbols 43 extern "C" struct android_namespace_t* android_get_exported_namespace( 44 const char* name); 45 46 extern "C" struct android_namespace_t* android_create_namespace( 47 const char* name, const char* ld_library_path, 48 const char* default_library_path, uint64_t type, 49 const char* permitted_when_isolated_path, 50 struct android_namespace_t* parent); 51 52 #if !defined(__LP64__) 53 static constexpr const char LIB[] = "lib"; 54 #else // !__LP64__ 55 static constexpr const char LIB[] = "lib64"; 56 #endif // !__LP64_ 57 58 static constexpr const char kApexSharedLibsRoot[] = "/apex/sharedlibs"; 59 60 // Before running the test, make sure that certain libraries are not pre-loaded 61 // in the test process. check_preloaded_libraries()62 void check_preloaded_libraries() { 63 static constexpr const char* unwanted[] = { 64 "libbase.so", 65 "libcrypto.so", 66 }; 67 68 std::ifstream f("/proc/self/maps"); 69 std::string line; 70 while (std::getline(f, line)) { 71 for (const char* lib : unwanted) { 72 EXPECT_TRUE(line.find(lib) == std::string::npos) 73 << "Library " << lib << " seems preloaded in the test process. " 74 << "This is a potential error. Please remove direct or transitive " 75 << "dependency to this library. You may debug this by running this " 76 << "test with `export LD_DEBUG=1` and " 77 << "`setprop debug.ld.all dlopen,dlerror`."; 78 } 79 } 80 } 81 TEST(apex_shared_libraries,symlink_libraries_loadable)82 TEST(apex_shared_libraries, symlink_libraries_loadable) { 83 check_preloaded_libraries(); 84 85 Fstab fstab; 86 ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab)); 87 88 // Regex to use when checking if a mount is for an active APEX or not. Note 89 // that non-active APEX mounts don't have the @<number> marker. 90 std::regex active_apex_pattern(R"(/apex/(.*)@\d+)"); 91 92 // Traverse mount points to identify apexs. 93 for (auto& entry : fstab) { 94 std::cmatch m; 95 if (!std::regex_match(entry.mount_point.c_str(), m, active_apex_pattern)) { 96 continue; 97 } 98 // Linker namespace name of the apex com.android.foo is com_android_foo. 99 std::string apex_namespace_name = m[1]; 100 std::replace(apex_namespace_name.begin(), apex_namespace_name.end(), '.', 101 '_'); 102 103 // Filter out any mount irrelevant (e.g. tmpfs) 104 std::string dev_file = fs::path(entry.blk_device).filename(); 105 if (!StartsWith(dev_file, "loop") && !StartsWith(dev_file, "dm-")) { 106 continue; 107 } 108 109 auto lib = fs::path(entry.mount_point) / LIB; 110 if (!fs::is_directory(lib)) { 111 continue; 112 } 113 114 for (auto& p : fs::directory_iterator(lib)) { 115 std::error_code ec; 116 if (!fs::is_symlink(p, ec)) { 117 continue; 118 } 119 120 // We are only checking libraries pointing at a location inside 121 // /apex/sharedlibs. 122 auto target = fs::read_symlink(p.path(), ec); 123 if (ec || !StartsWith(target.string(), kApexSharedLibsRoot)) { 124 continue; 125 } 126 127 LOG(INFO) << "Checking " << p.path(); 128 129 // Symlink validity check. 130 auto dest = fs::canonical(p.path(), ec); 131 EXPECT_FALSE(ec) << "Failed to resolve " << p.path() << " (symlink to " 132 << target << "): " << ec; 133 if (ec) { 134 continue; 135 } 136 137 // Library loading validity check. 138 dlerror(); // Clear any pending errors. 139 android_namespace_t* ns = 140 android_get_exported_namespace(apex_namespace_name.c_str()); 141 if (ns == nullptr) { 142 LOG(INFO) << "Creating linker namespace " << apex_namespace_name; 143 // In case the apex namespace doesn't exist (actually not accessible), 144 // create a new one that can search libraries from the apex directory 145 // and can load (but not search) from the shared lib APEX. 146 std::string search_paths = lib; 147 search_paths.push_back(':'); 148 // Adding "/system/lib[64]" is not ideal; we need to link to the 149 // namespace that is capable of loading libs from the directory. 150 // However, since the namespace (the `system` namespace) is not 151 // exported, we can't make a link. Instead, we allow this new namespace 152 // to search/load libraries from the directory. 153 search_paths.append(std::string("/system/") + LIB); 154 std::string permitted_paths = "/apex"; 155 ns = android_create_namespace( 156 apex_namespace_name.c_str(), 157 /* ld_library_path=*/nullptr, 158 /* default_library_path=*/search_paths.c_str(), 159 /* type=*/3, // ISOLATED and SHARED 160 /* permitted_when_isolated_path=*/permitted_paths.c_str(), 161 /* parent=*/nullptr); 162 } 163 164 EXPECT_TRUE(ns != nullptr) 165 << "Cannot find or create namespace " << apex_namespace_name; 166 const android_dlextinfo dlextinfo = { 167 .flags = ANDROID_DLEXT_USE_NAMESPACE, 168 .library_namespace = ns, 169 }; 170 171 void* handle = android_dlopen_ext(p.path().c_str(), RTLD_NOW, &dlextinfo); 172 EXPECT_TRUE(handle != nullptr) 173 << "Failed to load " << p.path() << " which is a symlink to " 174 << target << ".\n" 175 << "Reason: " << dlerror() << "\n" 176 << "Make sure that the library is accessible."; 177 if (handle == nullptr) { 178 continue; 179 } 180 auto guard = android::base::make_scope_guard([&]() { dlclose(handle); }); 181 182 // Check that library is loaded and pointing to the realpath of the 183 // library. 184 auto dl_callback = [](dl_phdr_info* info, size_t /* size */, void* data) { 185 auto dest = *static_cast<fs::path*>(data); 186 if (info->dlpi_name == nullptr) { 187 // This is linker imposing as libdl.so - skip it 188 return 0; 189 } 190 int j; 191 for (j = 0; j < info->dlpi_phnum; j++) { 192 void* addr = (void*)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); 193 Dl_info dl_info; 194 int rc = dladdr(addr, &dl_info); 195 if (rc == 0) { 196 continue; 197 } 198 if (dl_info.dli_fname) { 199 auto libpath = fs::path(dl_info.dli_fname); 200 if (libpath == dest) { 201 // Library found! 202 return 1; 203 } 204 } 205 } 206 207 return 0; 208 }; 209 bool found = (dl_iterate_phdr(dl_callback, &dest) == 1); 210 EXPECT_TRUE(found) << "Error verifying library symlink " << p.path() 211 << " which points to " << target 212 << " which resolves to file " << dest; 213 if (found) { 214 LOG(INFO) << "Verified that " << p.path() 215 << " correctly loads as library " << dest; 216 } 217 } 218 } 219 } 220