xref: /aosp_15_r20/system/apex/tests/native/apex_shared_libraries_test.cpp (revision 33f3758387333dbd2962d7edbd98681940d895da)
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