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 <string>
32
33 using android::base::GetBoolProperty;
34 using android::base::Split;
35 using android::base::StartsWith;
36 using android::fs_mgr::Fstab;
37 using android::fs_mgr::ReadFstabFromFile;
38
39 namespace fs = std::filesystem;
40
41 // No header available for these symbols
42 extern "C" struct android_namespace_t* android_get_exported_namespace(
43 const char* name);
44
45 extern "C" struct android_namespace_t* android_create_namespace(
46 const char* name, const char* ld_library_path,
47 const char* default_library_path, uint64_t type,
48 const char* permitted_when_isolated_path,
49 struct android_namespace_t* parent);
50
51 #if !defined(__LP64__)
52 static constexpr const char LIB[] = "lib";
53 #else // !__LP64__
54 static constexpr const char LIB[] = "lib64";
55 #endif // !__LP64_
56
57 static constexpr const char kApexSharedLibsRoot[] = "/apex/sharedlibs";
58
TEST(apex_shared_libraries,symlink_libraries_loadable)59 TEST(apex_shared_libraries, symlink_libraries_loadable) {
60 if (!GetBoolProperty("ro.apex.updatable", false)) {
61 GTEST_SKIP() << "Skipping test because device doesn't support APEX";
62 }
63
64 Fstab fstab;
65 ASSERT_TRUE(ReadFstabFromFile("/proc/mounts", &fstab));
66
67 // Regex to use when checking if a mount is for an active APEX or not. Note
68 // that non-active APEX mounts don't have the @<number> marker.
69 std::regex active_apex_pattern(R"(/apex/(.*)@\d+)");
70
71 // Traverse mount points to identify apexs.
72 for (auto& entry : fstab) {
73 std::cmatch m;
74 if (!std::regex_match(entry.mount_point.c_str(), m, active_apex_pattern)) {
75 continue;
76 }
77 // Linker namespace name of the apex com.android.foo is com_android_foo.
78 std::string apex_namespace_name = m[1];
79 std::replace(apex_namespace_name.begin(), apex_namespace_name.end(), '.',
80 '_');
81
82 // Filter out any mount irrelevant (e.g. tmpfs)
83 std::string dev_file = fs::path(entry.blk_device).filename();
84 if (!StartsWith(dev_file, "loop") && !StartsWith(dev_file, "dm-")) {
85 continue;
86 }
87
88 auto lib = fs::path(entry.mount_point) / LIB;
89 if (!fs::is_directory(lib)) {
90 continue;
91 }
92
93 for (auto& p : fs::directory_iterator(lib)) {
94 std::error_code ec;
95 if (!fs::is_symlink(p, ec)) {
96 continue;
97 }
98
99 // We are only checking libraries pointing at a location inside
100 // /apex/sharedlibs.
101 auto target = fs::read_symlink(p.path(), ec);
102 if (ec || !StartsWith(target.string(), kApexSharedLibsRoot)) {
103 continue;
104 }
105
106 // Symlink validity check.
107 auto dest = fs::canonical(p.path(), ec);
108 EXPECT_FALSE(ec) << "Failed to resolve " << p.path() << " (symlink to "
109 << target << "): " << ec;
110 if (ec) {
111 continue;
112 }
113
114 // Library loading validity check.
115 dlerror(); // Clear any pending errors.
116 android_namespace_t* ns =
117 android_get_exported_namespace(apex_namespace_name.c_str());
118 if (ns == nullptr) {
119 // In case the apex namespace doesn't exist (actually not accessible),
120 // create a new one that can search libraries from the apex directory
121 // and can load (but not search) from the shared lib APEX.
122 std::string search_paths = lib;
123 search_paths.push_back(':');
124 // Adding "/system/lib[64]" is not ideal; we need to link to the
125 // namespace that is capable of loading libs from the directory.
126 // However, since the namespace (the `system` namespace) is not
127 // exported, we can't make a link. Instead, we allow this new namespace
128 // to search/load libraries from the directory.
129 search_paths.append(std::string("/system/") + LIB);
130 std::string permitted_paths = "/apex";
131 ns = android_create_namespace(
132 apex_namespace_name.c_str(),
133 /* ld_library_path=*/nullptr,
134 /* default_library_path=*/search_paths.c_str(),
135 /* type=*/3, // ISOLATED and SHARED
136 /* permitted_when_isolated_path=*/permitted_paths.c_str(),
137 /* parent=*/nullptr);
138 }
139
140 EXPECT_TRUE(ns != nullptr)
141 << "Cannot find namespace " << apex_namespace_name;
142 const android_dlextinfo dlextinfo = {
143 .flags = ANDROID_DLEXT_USE_NAMESPACE,
144 .library_namespace = ns,
145 };
146
147 void* handle = android_dlopen_ext(p.path().c_str(), RTLD_NOW, &dlextinfo);
148 EXPECT_TRUE(handle != nullptr) << dlerror();
149 if (handle == nullptr) {
150 continue;
151 }
152 auto guard = android::base::make_scope_guard([&]() { dlclose(handle); });
153
154 // Check that library is loaded and pointing to the realpath of the
155 // library.
156 auto dl_callback = [](dl_phdr_info* info, size_t /* size */, void* data) {
157 auto dest = *static_cast<fs::path*>(data);
158 if (info->dlpi_name == nullptr) {
159 // This is linker imposing as libdl.so - skip it
160 return 0;
161 }
162 int j;
163 for (j = 0; j < info->dlpi_phnum; j++) {
164 void* addr = (void*)(info->dlpi_addr + info->dlpi_phdr[j].p_vaddr);
165 Dl_info dl_info;
166 int rc = dladdr(addr, &dl_info);
167 if (rc == 0) {
168 continue;
169 }
170 if (dl_info.dli_fname) {
171 auto libpath = fs::path(dl_info.dli_fname);
172 if (libpath == dest) {
173 // Library found!
174 return 1;
175 }
176 }
177 }
178
179 return 0;
180 };
181 bool found = (dl_iterate_phdr(dl_callback, &dest) == 1);
182 EXPECT_TRUE(found) << "Error verifying library symlink " << p.path()
183 << " which points to " << target
184 << " which resolves to file " << dest;
185 if (found) {
186 LOG(INFO) << "Verified that " << p.path()
187 << " correctly loads as library " << dest;
188 }
189 }
190 }
191 }
192