From f597fa5d1dca3b79b30d5b66e15e23b0bd91b560 Mon Sep 17 00:00:00 2001 From: Daniel Norman Date: Mon, 9 Nov 2020 17:28:24 -0800 Subject: [PATCH] Returns a service parse error on overrides across the treble boundary. Also includes new --out_ flags for system,system_ext,product,vendor,odm to allow host_init_verifier to work with a collection of init rc files. Test: host_init_verifier --out_system=... --out_vendor=... where vendor contains an init rc file that overrides a service present in system. Observe parse failure and non-zero exit. Bug: 163089173 Change-Id: I520fef613e0036df8a7d47a98d47405eaa969110 --- init/host_init_verifier.cpp | 86 ++++++++++++++++++++++++++++++------- init/service.cpp | 1 + init/service.h | 2 + init/service_parser.cpp | 8 ++++ init/subcontext.cpp | 3 ++ init/subcontext.h | 7 ++- 6 files changed, 89 insertions(+), 18 deletions(-) diff --git a/init/host_init_verifier.cpp b/init/host_init_verifier.cpp index ef9a4514c..db127d3f2 100644 --- a/init/host_init_verifier.cpp +++ b/init/host_init_verifier.cpp @@ -25,6 +25,8 @@ #include #include #include +#include +#include #include #include @@ -51,6 +53,7 @@ using namespace std::literals; +using android::base::EndsWith; using android::base::ParseInt; using android::base::ReadFileToString; using android::base::Split; @@ -61,6 +64,10 @@ using android::properties::PropertyInfoEntry; static std::vector passwd_files; +// NOTE: Keep this in sync with the order used by init.cpp LoadBootScripts() +static const std::vector partition_search_order = + std::vector({"system", "system_ext", "odm", "vendor", "product"}); + static std::vector> GetVendorPasswd(const std::string& passwd_file) { std::string passwd; if (!ReadFileToString(passwd_file, &passwd)) { @@ -148,13 +155,24 @@ static Result check_stub(const BuiltinArguments& args) { #include "generated_stub_builtin_function_map.h" void PrintUsage() { - std::cout << "usage: host_init_verifier [options] \n" - "\n" - "Tests an init script for correctness\n" - "\n" - "-p FILE\tSearch this passwd file for users and groups\n" - "--property_contexts=FILE\t Use this file for property_contexts\n" - << std::endl; + fprintf(stdout, R"(usage: host_init_verifier [options] + +Tests init script(s) for correctness. + +Generic options: + -p FILE Search this passwd file for users and groups. + --property_contexts=FILE Use this file for property_contexts. + +Single script mode options: + [init rc file] Positional argument; test this init script. + +Multiple script mode options: + --out_system=DIR Path to the output product directory for the system partition. + --out_system_ext=DIR Path to the output product directory for the system_ext partition. + --out_odm=DIR Path to the output product directory for the odm partition. + --out_vendor=DIR Path to the output product directory for the vendor partition. + --out_product=DIR Path to the output product directory for the product partition. +)"); } Result ReadInterfaceInheritanceHierarchy() { @@ -203,12 +221,18 @@ int main(int argc, char** argv) { android::base::SetMinimumLogSeverity(android::base::ERROR); auto property_infos = std::vector(); + std::map partition_map; while (true) { static const char kPropertyContexts[] = "property-contexts="; static const struct option long_options[] = { {"help", no_argument, nullptr, 'h'}, {kPropertyContexts, required_argument, nullptr, 0}, + {"out_system", required_argument, nullptr, 0}, + {"out_system_ext", required_argument, nullptr, 0}, + {"out_odm", required_argument, nullptr, 0}, + {"out_vendor", required_argument, nullptr, 0}, + {"out_product", required_argument, nullptr, 0}, {nullptr, 0, nullptr, 0}, }; @@ -224,6 +248,16 @@ int main(int argc, char** argv) { if (long_options[option_index].name == kPropertyContexts) { HandlePropertyContexts(optarg, &property_infos); } + for (const auto& p : partition_search_order) { + if (long_options[option_index].name == "out_" + p) { + if (partition_map.find(p) != partition_map.end()) { + PrintUsage(); + return EXIT_FAILURE; + } + partition_map[p] = + EndsWith(optarg, "/") ? optarg : std::string(optarg) + "/"; + } + } break; case 'h': PrintUsage(); @@ -240,7 +274,9 @@ int main(int argc, char** argv) { argc -= optind; argv += optind; - if (argc != 1) { + // If provided, use the partition map to check multiple init rc files. + // Otherwise, check a single init rc file. + if ((!partition_map.empty() && argc != 0) || (partition_map.empty() && argc != 1)) { PrintUsage(); return EXIT_FAILURE; } @@ -262,24 +298,42 @@ int main(int argc, char** argv) { property_info_area = reinterpret_cast(serialized_contexts.c_str()); + if (!partition_map.empty()) { + std::vector vendor_prefixes; + for (const auto& partition : {"vendor", "odm"}) { + if (partition_map.find(partition) != partition_map.end()) { + vendor_prefixes.push_back(partition_map.at(partition)); + } + } + InitializeHostSubcontext(vendor_prefixes); + } + const BuiltinFunctionMap& function_map = GetBuiltinFunctionMap(); Action::set_function_map(&function_map); ActionManager& am = ActionManager::GetInstance(); ServiceList& sl = ServiceList::GetInstance(); Parser parser; - parser.AddSectionParser("service", std::make_unique( - &sl, nullptr, *interface_inheritance_hierarchy_map)); - parser.AddSectionParser("on", std::make_unique(&am, nullptr)); + parser.AddSectionParser("service", + std::make_unique(&sl, GetSubcontext(), + *interface_inheritance_hierarchy_map)); + parser.AddSectionParser("on", std::make_unique(&am, GetSubcontext())); parser.AddSectionParser("import", std::make_unique()); - if (!parser.ParseConfigFileInsecure(*argv)) { - LOG(ERROR) << "Failed to open init rc script '" << *argv << "'"; - return EXIT_FAILURE; + if (!partition_map.empty()) { + for (const auto& p : partition_search_order) { + if (partition_map.find(p) != partition_map.end()) { + parser.ParseConfig(partition_map.at(p) + "etc/init"); + } + } + } else { + if (!parser.ParseConfigFileInsecure(*argv)) { + LOG(ERROR) << "Failed to open init rc script '" << *argv << "'"; + return EXIT_FAILURE; + } } size_t failures = parser.parse_error_count() + am.CheckAllCommands() + sl.CheckAllCommands(); if (failures > 0) { - LOG(ERROR) << "Failed to parse init script '" << *argv << "' with " << failures - << " errors"; + LOG(ERROR) << "Failed to parse init scripts with " << failures << " error(s)."; return EXIT_FAILURE; } return EXIT_SUCCESS; diff --git a/init/service.cpp b/init/service.cpp index 7b983921b..766eb5d93 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -154,6 +154,7 @@ Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid, .priority = 0}, namespaces_{.flags = namespace_flags}, seclabel_(seclabel), + subcontext_(subcontext_for_restart_commands), onrestart_(false, subcontext_for_restart_commands, "", 0, "onrestart", {}), oom_score_adjust_(DEFAULT_OOM_SCORE_ADJUST), diff --git a/init/service.h b/init/service.h index bc5c90fe6..aee1e5dfa 100644 --- a/init/service.h +++ b/init/service.h @@ -137,6 +137,7 @@ class Service { flags_ &= ~SVC_ONESHOT; } } + Subcontext* subcontext() const { return subcontext_; } private: void NotifyStateChange(const std::string& new_state) const; @@ -168,6 +169,7 @@ class Service { std::vector files_; std::vector> environment_vars_; + Subcontext* subcontext_; Action onrestart_; // Commands to execute on restart. std::vector writepid_files_; diff --git a/init/service_parser.cpp b/init/service_parser.cpp index 97621dac6..57c311a52 100644 --- a/init/service_parser.cpp +++ b/init/service_parser.cpp @@ -657,6 +657,14 @@ Result ServiceParser::EndSection() { << "' with a config in APEX"; } + std::string context = service_->subcontext() ? service_->subcontext()->context() : ""; + std::string old_context = + old_service->subcontext() ? old_service->subcontext()->context() : ""; + if (context != old_context) { + return Error() << "service '" << service_->name() << "' overrides another service " + << "across the treble boundary."; + } + service_list_->RemoveService(*old_service); old_service = nullptr; } diff --git a/init/subcontext.cpp b/init/subcontext.cpp index dc2455e2f..f1fbffe60 100644 --- a/init/subcontext.cpp +++ b/init/subcontext.cpp @@ -342,6 +342,9 @@ void InitializeSubcontext() { new Subcontext(std::vector{"/vendor", "/odm"}, kVendorContext)); } } +void InitializeHostSubcontext(std::vector vendor_prefixes) { + subcontext.reset(new Subcontext(vendor_prefixes, kVendorContext, /*host=*/true)); +} Subcontext* GetSubcontext() { return subcontext.get(); diff --git a/init/subcontext.h b/init/subcontext.h index 788d3be6c..cb4138e69 100644 --- a/init/subcontext.h +++ b/init/subcontext.h @@ -36,9 +36,11 @@ static constexpr const char kTestContext[] = "test-test-test"; class Subcontext { public: - Subcontext(std::vector path_prefixes, std::string context) + Subcontext(std::vector path_prefixes, std::string context, bool host = false) : path_prefixes_(std::move(path_prefixes)), context_(std::move(context)), pid_(0) { - Fork(); + if (!host) { + Fork(); + } } Result Execute(const std::vector& args); @@ -61,6 +63,7 @@ class Subcontext { int SubcontextMain(int argc, char** argv, const BuiltinFunctionMap* function_map); void InitializeSubcontext(); +void InitializeHostSubcontext(std::vector vendor_prefixes); Subcontext* GetSubcontext(); bool SubcontextChildReap(pid_t pid); void SubcontextTerminate();