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 } // namespace
134
135 namespace android {
136 namespace linkerconfig {
137 namespace modules {
138
ScanActiveApexes(const std::string & root)139 Result<std::map<std::string, ApexInfo>> ScanActiveApexes(const std::string& root) {
140 std::map<std::string, ApexInfo> apexes;
141 const auto apex_root = root + apex::kApexRoot;
142 for (const auto& [path, manifest] : apex::GetActivePackages(apex_root)) {
143 bool has_bin = PathExists(path + "/bin");
144 bool has_lib = PathExists(path + "/lib") || PathExists(path + "/lib64");
145 bool has_shared_lib = manifest.requiresharedapexlibs().size() != 0;
146
147 std::vector<std::string> permitted_paths;
148 bool visible = false;
149 std::vector<Contribution> contributions;
150
151 std::string linker_config_path = path + "/etc/linker.config.pb";
152 if (PathExists(linker_config_path)) {
153 auto linker_config = ParseLinkerConfig(linker_config_path);
154
155 if (linker_config.ok()) {
156 permitted_paths = {linker_config->permittedpaths().begin(),
157 linker_config->permittedpaths().end()};
158 for (const std::string& path : permitted_paths) {
159 Result<void> verify_permitted_path = VerifyPath(path);
160 if (!verify_permitted_path.ok()) {
161 return Error() << "Failed to validate path from APEX linker config"
162 << linker_config_path << " : "
163 << verify_permitted_path.error();
164 }
165 }
166 visible = linker_config->visible();
167 for (auto& contribution : linker_config->contributions()) {
168 Contribution c;
169 c.namespace_name = contribution.namespace_();
170 c.paths = {contribution.paths().begin(), contribution.paths().end()};
171 contributions.emplace_back(std::move(c));
172 }
173 } else {
174 return Error() << "Failed to read APEX linker config : "
175 << linker_config.error();
176 }
177 }
178
179 ApexInfo info(manifest.name(),
180 TrimPrefix(path, root),
181 {manifest.providenativelibs().begin(),
182 manifest.providenativelibs().end()},
183 {manifest.requirenativelibs().begin(),
184 manifest.requirenativelibs().end()},
185 {manifest.jnilibs().begin(), manifest.jnilibs().end()},
186 std::move(permitted_paths),
187 std::move(contributions),
188 has_bin,
189 has_lib,
190 visible,
191 has_shared_lib);
192 apexes.emplace(manifest.name(), std::move(info));
193 }
194
195 // After scanning apexes, we still need to augment ApexInfo based on other
196 // input files
197 // - original_path: based on /apex/apex-info-list.xml
198 // - public_libs: based on /system/etc/public.libraries.txt
199
200 if (!apexes.empty()) {
201 const std::string info_list_file = apex_root + "/apex-info-list.xml";
202 auto info_list =
203 com::android::apex::readApexInfoList(info_list_file.c_str());
204 if (info_list.has_value()) {
205 for (const auto& info : info_list->getApexInfo()) {
206 // skip inactive apexes
207 if (!info.getIsActive()) {
208 continue;
209 }
210 // skip "sharedlibs" apexes
211 if (info.getProvideSharedApexLibs()) {
212 continue;
213 }
214 // Get the pre-installed path of the apex. Normally (i.e. in Android),
215 // failing to find the pre-installed path is an assertion failure
216 // because apexd demands that every apex to have a pre-installed one.
217 // However, when this runs in a VM where apexes are seen as virtio block
218 // devices, the situation is different. If the APEX in the host side is
219 // an updated (or staged) one, the block device representing the APEX on
220 // the VM side doesn't have the pre-installed path because the factory
221 // version of the APEX wasn't exported to the VM. Therefore, we use the
222 // module path as original_path when we are running in a VM which can be
223 // guessed by checking if the path is /dev/block/vdN.
224 std::string path;
225 if (info.hasPreinstalledModulePath()) {
226 path = info.getPreinstalledModulePath();
227 } else if (StartsWith(info.getModulePath(), "/dev/block/vd")) {
228 path = info.getModulePath();
229 } else {
230 return Error() << "Failed to determine original path for apex "
231 << info.getModuleName() << " at " << info_list_file;
232 }
233 apexes[info.getModuleName()].original_path = std::move(path);
234 }
235 } else {
236 return ErrnoError() << "Can't read " << info_list_file;
237 }
238
239 const std::string public_libraries_file =
240 root + "/system/etc/public.libraries.txt";
241 // Do not fail when public.libraries.txt is missing for minimal Android
242 // environment with no ART.
243 if (PathExists(public_libraries_file)) {
244 auto public_libraries = ReadPublicLibraries(public_libraries_file);
245 if (!public_libraries.ok()) {
246 return Error() << "Can't read " << public_libraries_file << ": "
247 << public_libraries.error();
248 }
249 for (auto& [name, apex] : apexes) {
250 // Only system apexes can provide public libraries.
251 if (!apex.InSystem()) {
252 continue;
253 }
254 apex.public_libs = Intersect(apex.provide_libs, *public_libraries);
255 }
256 }
257 }
258
259 return apexes;
260 }
261
InSystem() const262 bool ApexInfo::InSystem() const {
263 // /system partition
264 if (StartsWith(original_path, "/system/apex/")) {
265 return true;
266 }
267 // /system_ext partition
268 if (StartsWith(original_path, "/system_ext/apex/") ||
269 StartsWith(original_path, "/system/system_ext/apex/")) {
270 return true;
271 }
272 // /product partition if it's not separated from "system"
273 if (!IsProductVndkVersionDefined()) {
274 if (StartsWith(original_path, "/product/apex/") ||
275 StartsWith(original_path, "/system/product/apex/")) {
276 return true;
277 }
278 }
279 // Guest mode Android may have system APEXes from host via block APEXes
280 if (StartsWith(original_path, "/dev/block/vd")) {
281 return true;
282 }
283 return false;
284 }
285
InProduct() const286 bool ApexInfo::InProduct() const {
287 // /product partition if it's separated from "system"
288 if (IsProductVndkVersionDefined()) {
289 if (StartsWith(original_path, "/product/apex/") ||
290 StartsWith(original_path, "/system/product/apex/")) {
291 return true;
292 }
293 }
294 return false;
295 }
296
InVendor() const297 bool ApexInfo::InVendor() const {
298 // /vendor partition
299 if (StartsWith(original_path, "/vendor/apex/") ||
300 StartsWith(original_path, "/system/vendor/apex/")) {
301 return true;
302 }
303 // /odm/apex is not supported yet.
304 return false;
305 }
306
307 } // namespace modules
308 } // namespace linkerconfig
309 } // namespace android
310