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 <dlfcn.h> 23 #include <fstab/fstab.h> 24 #include <gtest/gtest.h> 25 #include <link.h> 26 27 #include <filesystem> 28 #include <fstream> 29 #include <string> 30 31 using android::base::GetBoolProperty; 32 using android::base::Split; 33 using android::base::StartsWith; 34 using android::fs_mgr::Fstab; 35 using android::fs_mgr::ReadFstabFromFile; 36 37 namespace fs = std::filesystem; 38 39 static constexpr const char* kApexRoot = "/apex"; 40 static constexpr const char* kApexSharedLibsRoot = "/apex/sharedlibs"; 41 42 TEST(apex_shared_libraries, symlink_libraries_loadable) { 43 if (!GetBoolProperty("ro.apex.updatable", false)) { 44 GTEST_SKIP() << "Skipping test because device doesn't support APEX"; 45 } 46 47 Fstab fstab; 48 ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab)); 49 50 // Traverse mount points to identify apexs. 51 for (auto& entry : fstab) { 52 if (fs::path(entry.mount_point).parent_path() != kApexRoot) { 53 continue; 54 } 55 56 // Focus on "active" apexs. 57 if (entry.mount_point.find('@') != std::string::npos) { 58 continue; 59 } 60 std::string dev_file = fs::path(entry.blk_device).filename(); 61 62 // Filter out any mount irrelevant (e.g. tmpfs) 63 if (!StartsWith(dev_file, "loop") && !StartsWith(dev_file, "dm-")) { 64 continue; 65 } 66 67 #if !defined(__LP64__) 68 auto lib = fs::path(entry.mount_point) / "lib"; 69 #else // !__LP64__ 70 auto lib = fs::path(entry.mount_point) / "lib64"; 71 #endif // !__LP64__ 72 if (!fs::is_directory(lib)) { 73 continue; 74 } 75 for (auto& p : fs::directory_iterator(lib)) { 76 std::error_code ec; 77 if (!fs::is_symlink(p, ec)) { 78 continue; 79 } 80 81 // We are only checking libraries pointing at a location inside 82 // /apex/sharedlibs. 83 auto target = fs::read_symlink(p.path(), ec); 84 if (ec || !StartsWith(target.string(), kApexSharedLibsRoot)) { 85 continue; 86 } 87 88 // Symlink validity check. 89 auto dest = fs::canonical(p.path(), ec); 90 EXPECT_FALSE(ec) << "Failed to resolve " << p.path() << " (symlink to " 91 << target << "): " << ec; 92 if (ec) { 93 continue; 94 } 95 96 // Library loading validity check. 97 dlerror(); // Clear any pending errors. 98 void* handle = dlopen(p.path().c_str(), RTLD_NOW); 99 EXPECT_TRUE(handle != nullptr) << dlerror(); 100 if (handle == nullptr) { 101 continue; 102 } 103 auto guard = android::base::make_scope_guard([&]() { dlclose(handle); }); 104 105 // Check that library is loaded and pointing to the realpath of the 106 // library. 107 auto dl_callback = [](dl_phdr_info* info, size_t /* size */, void* data) { 108 auto dest = *static_cast<fs::path*>(data); 109 if (info->dlpi_name == nullptr) { 110 // This is linker imposing as libdl.so - skip it 111 return 0; 112 } 113 int j; 114 for (j = 0; j < info->dlpi_phnum; j++) { 115 void* addr = (void*)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr); 116 Dl_info dl_info; 117 int rc = dladdr(addr, &dl_info); 118 if (rc == 0) { 119 continue; 120 } 121 if (dl_info.dli_fname) { 122 auto libpath = fs::path(dl_info.dli_fname); 123 if (libpath == dest) { 124 // Library found! 125 return 1; 126 } 127 } 128 } 129 130 return 0; 131 }; 132 bool found = (dl_iterate_phdr(dl_callback, &dest) == 1); 133 EXPECT_TRUE(found) << "Error verifying library symlink " << p.path() 134 << " which points to " << target 135 << " which resolves to file " << dest; 136 if (found) { 137 LOG(INFO) << "Verified that " << p.path() 138 << " correctly loads as library " << dest; 139 } 140 } 141 } 142 } 143