/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "linkerconfig/apex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "linkerconfig/configparser.h" #include "linkerconfig/environment.h" #include "linkerconfig/log.h" #include "linkerconfig/stringutil.h" // include after log.h to avoid macro redefinition error #include "com_android_apex.h" using android::base::ErrnoError; using android::base::Error; using android::base::ReadFileToString; using android::base::Result; using android::base::StartsWith; namespace { bool PathExists(const std::string& path) { return access(path.c_str(), F_OK) == 0; } Result> ReadPublicLibraries(const std::string& filepath) { std::string file_content; if (!android::base::ReadFileToString(filepath, &file_content)) { return ErrnoError(); } std::vector lines = android::base::Split(file_content, "\n"); std::set sonames; for (auto& line : lines) { auto trimmed_line = android::base::Trim(line); if (trimmed_line[0] == '#' || trimmed_line.empty()) { continue; } std::vector tokens = android::base::Split(trimmed_line, " "); if (tokens.size() < 1 || tokens.size() > 3) { return Errorf("Malformed line \"{}\"", line); } sonames.insert(tokens[0]); } return sonames; } std::vector Intersect(const std::vector& as, const std::set& bs) { std::vector intersect; std::copy_if(as.begin(), as.end(), std::back_inserter(intersect), [&bs](const auto& a) { return bs.find(a) != bs.end(); }); return intersect; } bool IsValidForPath(const uint_fast8_t c) { if (c >= 'a' && c <= 'z') return true; if (c >= 'A' && c <= 'Z') return true; if (c >= '0' && c <= '9') return true; if (c == '-' || c == '_' || c == '.') return true; return false; } Result VerifyPath(const std::string& path) { const size_t length = path.length(); constexpr char lib_dir[] = "${LIB}"; constexpr size_t lib_dir_len = (sizeof lib_dir) - 1; const std::string_view path_view(path); if (length == 0) { return Error() << "Empty path is not allowed"; } for (size_t i = 0; i < length; i++) { uint_fast8_t current_char = path[i]; if (current_char == '/') { i++; if (i >= length) { return {}; } else if (path[i] == '/') { return Error() << "'/' should not appear twice in " << path; } else if (i + lib_dir_len <= length && path_view.substr(i, lib_dir_len) == lib_dir) { i += lib_dir_len - 1; } else { for (; i < length; i++) { current_char = path[i]; if (current_char == '/') { i--; break; } if (!IsValidForPath(current_char)) { return Error() << "Invalid char '" << current_char << "' in " << path; } } } } else { return Error() << "Invalid char '" << current_char << "' in " << path << " at " << i; } } return {}; } } // namespace namespace android { namespace linkerconfig { namespace modules { Result> ScanActiveApexes(const std::string& root) { std::map apexes; const auto apex_root = root + apex::kApexRoot; for (const auto& [path, manifest] : apex::GetActivePackages(apex_root)) { bool has_bin = PathExists(path + "/bin"); bool has_lib = PathExists(path + "/lib") || PathExists(path + "/lib64"); bool has_shared_lib = manifest.requiresharedapexlibs().size() != 0; std::vector permitted_paths; bool visible = false; std::string linker_config_path = path + "/etc/linker.config.pb"; if (PathExists(linker_config_path)) { auto linker_config = ParseLinkerConfig(linker_config_path); if (linker_config.ok()) { permitted_paths = {linker_config->permittedpaths().begin(), linker_config->permittedpaths().end()}; for (const std::string& path : permitted_paths) { Result verify_permitted_path = VerifyPath(path); if (!verify_permitted_path.ok()) { return Error() << "Failed to validate path from APEX linker config" << linker_config_path << " : " << verify_permitted_path.error(); } } visible = linker_config->visible(); } else { return Error() << "Failed to read APEX linker config : " << linker_config.error(); } } ApexInfo info(manifest.name(), TrimPrefix(path, root), {manifest.providenativelibs().begin(), manifest.providenativelibs().end()}, {manifest.requirenativelibs().begin(), manifest.requirenativelibs().end()}, {manifest.jnilibs().begin(), manifest.jnilibs().end()}, std::move(permitted_paths), has_bin, has_lib, visible, has_shared_lib); apexes.emplace(manifest.name(), std::move(info)); } if (!apexes.empty()) { const std::string info_list_file = apex_root + "/apex-info-list.xml"; auto info_list = com::android::apex::readApexInfoList(info_list_file.c_str()); if (info_list.has_value()) { for (const auto& info : info_list->getApexInfo()) { apexes[info.getModuleName()].original_path = info.getPreinstalledModulePath(); } } else { return ErrnoError() << "Can't read " << info_list_file; } const std::string public_libraries_file = root + "/system/etc/public.libraries.txt"; auto public_libraries = ReadPublicLibraries(public_libraries_file); if (public_libraries.ok()) { for (auto& [name, apex] : apexes) { // Only system apexes can provide public libraries. if (!apex.InSystem()) { continue; } apex.public_libs = Intersect(apex.provide_libs, *public_libraries); } } else { // Do not fail when public.libraries.txt is missing for minimal Android // environment with no ART. LOG(WARNING) << "Can't read " << public_libraries_file << ": " << public_libraries.error(); } } return apexes; } bool ApexInfo::InSystem() const { return StartsWith(original_path, "/system/apex/") || StartsWith(original_path, "/system_ext/apex/") || (!IsProductVndkVersionDefined() && StartsWith(original_path, "/product/apex/")); } bool ApexInfo::InProduct() const { return IsProductVndkVersionDefined() && StartsWith(original_path, "/product/apex/"); } bool ApexInfo::InVendor() const { return StartsWith(original_path, "/vendor/apex/") || StartsWith(original_path, "/odm/apex/"); } } // namespace modules } // namespace linkerconfig } // namespace android