• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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