xref: /aosp_15_r20/system/linkerconfig/modules/apex.cc (revision e5eeaa8e05bc25a862c0c861bda7c8a6bfb42dad)
1 /*
2  * Copyright (C) 2020 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 #include "linkerconfig/apex.h"
17 
18 #include <algorithm>
19 #include <cstring>
20 #include <regex>
21 #include <set>
22 #include <string>
23 #include <string_view>
24 #include <vector>
25 
26 #include <android-base/file.h>
27 #include <android-base/result.h>
28 #include <android-base/strings.h>
29 #include <apexutil.h>
30 #include <unistd.h>
31 
32 #include "linkerconfig/configparser.h"
33 #include "linkerconfig/environment.h"
34 #include "linkerconfig/log.h"
35 #include "linkerconfig/stringutil.h"
36 
37 // include after log.h to avoid macro redefinition error
38 #include "com_android_apex.h"
39 
40 using android::base::ErrnoError;
41 using android::base::Error;
42 using android::base::ReadFileToString;
43 using android::base::Result;
44 using android::base::StartsWith;
45 
46 namespace {
PathExists(const std::string & path)47 bool PathExists(const std::string& path) {
48   return access(path.c_str(), F_OK) == 0;
49 }
50 
ReadPublicLibraries(const std::string & filepath)51 Result<std::set<std::string>> ReadPublicLibraries(const std::string& filepath) {
52   std::string file_content;
53   if (!android::base::ReadFileToString(filepath, &file_content, true)) {
54     return ErrnoError();
55   }
56   std::vector<std::string> lines = android::base::Split(file_content, "\n");
57   std::set<std::string> sonames;
58   for (auto& line : lines) {
59     auto trimmed_line = android::base::Trim(line);
60     if (trimmed_line.empty() || trimmed_line[0] == '#') {
61       continue;
62     }
63     std::vector<std::string> tokens = android::base::Split(trimmed_line, " ");
64     if (tokens.size() < 1 || tokens.size() > 3) {
65       return Errorf("Malformed line \"{}\"", line);
66     }
67     sonames.insert(tokens[0]);
68   }
69   return sonames;
70 }
71 
Intersect(const std::vector<std::string> & as,const std::set<std::string> & bs)72 std::vector<std::string> Intersect(const std::vector<std::string>& as,
73                                    const std::set<std::string>& bs) {
74   std::vector<std::string> intersect;
75   std::copy_if(as.begin(),
76                as.end(),
77                std::back_inserter(intersect),
78                [&bs](const auto& a) { return bs.find(a) != bs.end(); });
79   return intersect;
80 }
81 
IsValidForPath(const uint_fast8_t c)82 bool IsValidForPath(const uint_fast8_t c) {
83   if (c >= 'a' && c <= 'z') return true;
84   if (c >= 'A' && c <= 'Z') return true;
85   if (c >= '0' && c <= '9') return true;
86   if (c == '-' || c == '_' || c == '.') return true;
87   return false;
88 }
89 
VerifyPath(const std::string & path)90 Result<void> VerifyPath(const std::string& path) {
91   const size_t length = path.length();
92   constexpr char lib_dir[] = "${LIB}";
93   constexpr size_t lib_dir_len = (sizeof lib_dir) - 1;
94   const std::string_view path_view(path);
95 
96   if (length == 0) {
97     return Error() << "Empty path is not allowed";
98   }
99 
100   for (size_t i = 0; i < length; i++) {
101     uint_fast8_t current_char = path[i];
102     if (current_char == '/') {
103       i++;
104       if (i >= length) {
105         return {};
106       } else if (path[i] == '/') {
107         return Error() << "'/' should not appear twice in " << path;
108       } else if (i + lib_dir_len <= length &&
109                  path_view.substr(i, lib_dir_len) == lib_dir) {
110         i += lib_dir_len - 1;
111       } else {
112         for (; i < length; i++) {
113           current_char = path[i];
114           if (current_char == '/') {
115             i--;
116             break;
117           }
118 
119           if (!IsValidForPath(current_char)) {
120             return Error() << "Invalid char '" << current_char << "' in "
121                            << path;
122           }
123         }
124       }
125     } else {
126       return Error() << "Invalid char '" << current_char << "' in " << path
127                      << " at " << i;
128     }
129   }
130 
131   return {};
132 }
133 
ReadPublicLibrariesOpt(const std::string & filepath)134 Result<std::set<std::string>> ReadPublicLibrariesOpt(const std::string& filepath) {
135   // Do not fail when public.libraries.txt is missing for minimal Android
136   // environment with no ART.
137   if (!PathExists(filepath)) {
138     return {};
139   }
140   // But fails with error while reading the file
141   auto public_libraries = ReadPublicLibraries(filepath);
142   if (!public_libraries.ok()) {
143     return Error() << "Can't read " << filepath << ": "
144                    << public_libraries.error();
145   }
146   return public_libraries;
147 }
148 }  // namespace
149 
150 namespace android {
151 namespace linkerconfig {
152 namespace modules {
153 
ScanActiveApexes(const std::string & root)154 Result<std::map<std::string, ApexInfo>> ScanActiveApexes(const std::string& root) {
155   std::map<std::string, ApexInfo> apexes;
156   const auto apex_root = root + apex::kApexRoot;
157   for (const auto& [path, manifest] : apex::GetActivePackages(apex_root)) {
158     bool has_bin = PathExists(path + "/bin");
159     bool has_lib = PathExists(path + "/lib") || PathExists(path + "/lib64");
160     bool has_shared_lib = manifest.requiresharedapexlibs().size() != 0;
161 
162     std::vector<std::string> permitted_paths;
163     bool visible = false;
164 
165     std::string linker_config_path = path + "/etc/linker.config.pb";
166     if (PathExists(linker_config_path)) {
167       auto linker_config = ParseLinkerConfig(linker_config_path);
168 
169       if (linker_config.ok()) {
170         permitted_paths = {linker_config->permittedpaths().begin(),
171                            linker_config->permittedpaths().end()};
172         for (const std::string& path : permitted_paths) {
173           Result<void> verify_permitted_path = VerifyPath(path);
174           if (!verify_permitted_path.ok()) {
175             return Error() << "Failed to validate path from APEX linker config"
176                            << linker_config_path << " : "
177                            << verify_permitted_path.error();
178           }
179         }
180         visible = linker_config->visible();
181       } else {
182         return Error() << "Failed to read APEX linker config : "
183                        << linker_config.error();
184       }
185     }
186 
187     ApexInfo info(manifest.name(),
188                   TrimPrefix(path, root),
189                   {manifest.providenativelibs().begin(),
190                    manifest.providenativelibs().end()},
191                   {manifest.requirenativelibs().begin(),
192                    manifest.requirenativelibs().end()},
193                   {manifest.jnilibs().begin(), manifest.jnilibs().end()},
194                   std::move(permitted_paths),
195                   has_bin,
196                   has_lib,
197                   visible,
198                   has_shared_lib);
199     apexes.emplace(manifest.name(), std::move(info));
200   }
201 
202   // After scanning apexes, we still need to augment ApexInfo based on other
203   // input files
204   // - original_path: based on /apex/apex-info-list.xml
205   // - public_libs: based on /{system, vendor}/etc/public.libraries.txt
206 
207   if (!apexes.empty()) {
208     // 1. set original_path
209     const std::string info_list_file = apex_root + "/apex-info-list.xml";
210     auto info_list =
211         com::android::apex::readApexInfoList(info_list_file.c_str());
212     if (info_list.has_value()) {
213       for (const auto& info : info_list->getApexInfo()) {
214         // skip inactive apexes
215         if (!info.getIsActive()) {
216           continue;
217         }
218         // skip "sharedlibs" apexes
219         if (info.getProvideSharedApexLibs()) {
220           continue;
221         }
222         apexes[info.getModuleName()].partition = std::move(info.getPartition());
223       }
224     } else {
225       return ErrnoError() << "Can't read " << info_list_file;
226     }
227 
228     // 2. set public_libs
229     auto system_public_libs =
230         ReadPublicLibrariesOpt(root + "/system/etc/public.libraries.txt");
231     if (!system_public_libs.ok()) return system_public_libs.error();
232 
233     auto vendor_public_libs =
234         ReadPublicLibrariesOpt(root + "/vendor/etc/public.libraries.txt");
235     if (!vendor_public_libs.ok()) return vendor_public_libs.error();
236 
237     for (auto& [name, apex] : apexes) {
238       if (apex.InSystem()) {
239         apex.public_libs = Intersect(apex.provide_libs, *system_public_libs);
240       } else if (apex.InVendor()) {
241         apex.public_libs = Intersect(apex.provide_libs, *vendor_public_libs);
242       }
243     }
244   }
245 
246   return apexes;
247 }
248 
InSystem() const249 bool ApexInfo::InSystem() const {
250   // /system partition
251   if (partition.compare("SYSTEM") == 0) {
252     return true;
253   }
254   // /system_ext partition
255   if (partition.compare("SYSTEM_EXT") == 0) {
256     return true;
257   }
258   // /product partition if it's not separated from "system"
259   if (!IsTreblelizedDevice() && partition.compare("PRODUCT") == 0) {
260     return true;
261   }
262   return false;
263 }
264 
InProduct() const265 bool ApexInfo::InProduct() const {
266   // /product partition if it's separated from "system"
267   if (IsTreblelizedDevice()) {
268     if (partition.compare("PRODUCT") == 0) {
269       return true;
270     }
271   }
272   return false;
273 }
274 
InVendor() const275 bool ApexInfo::InVendor() const {
276   // /vendor and /odm partition
277   if (partition.compare("VENDOR") == 0 || partition.compare("ODM") == 0) {
278     return true;
279   }
280   return false;
281 }
282 
283 }  // namespace modules
284 }  // namespace linkerconfig
285 }  // namespace android
286