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 
TEST(apex_shared_libraries,symlink_libraries_loadable)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