From 70788f93ba5e8262790db9a473715362b8372501 Mon Sep 17 00:00:00 2001 From: Martijn Coenen Date: Tue, 23 Apr 2019 16:26:01 +0200 Subject: [PATCH] Support for stopping/starting post-data-mount class subsets. On devices that use FDE and APEX at the same time, we need to bring up a minimal framework to be able to mount the /data partition. During this period, a tmpfs /data filesystem is created, which doesn't contain any of the updated APEXEs. As a consequence, all those processes will be using the APEXes from the /system partition. This is obviously not desired, as APEXes in /system may be old and/or contain security issues. Additionally, it would create a difference between FBE and FDE devices at runtime. Ideally, we restart all processes that have started after we created the tmpfs /data. We can't (re)start based on class names alone, because some classes (eg 'hal') contain services that are required to start apexd itself and that shouldn't be killed (eg the graphics HAL). To address this, keep track of which processes are started after /data is mounted, with a new 'mark_post_data' keyword. Additionally, create 'class_reset_post_data', which resets all services in the class that were created after the initial /data mount, and 'class_start_post_data', which starts all services in the class that were started after /data was mounted. On a device with FBE, these keywords wouldn't be used; on a device with FDE, we'd use them to bring down the right processes after the user has entered the correct secret, and restart them. Bug: 118485723 Test: manually verified process list Change-Id: I16adb776dacf1dd1feeaff9e60639b99899905eb --- init/README.md | 12 ++++++++++++ init/builtins.cpp | 39 +++++++++++++++++++++++++++++++++++---- init/service.cpp | 18 +++++++++++++++++- init/service.h | 7 +++++++ rootdir/init.rc | 9 ++++++--- 5 files changed, 77 insertions(+), 8 deletions(-) diff --git a/init/README.md b/init/README.md index 51deb5a12..5f8abf20e 100644 --- a/init/README.md +++ b/init/README.md @@ -412,6 +412,10 @@ Commands not already running. See the start entry for more information on starting services. +`class_start_post_data ` +> Like `class_start`, but only considers services that were started + after /data was mounted. Only used for FDE devices. + `class_stop ` > Stop and disable all services of the specified class if they are currently running. @@ -421,6 +425,10 @@ Commands currently running, without disabling them. They can be restarted later using `class_start`. +`class_reset_post_data ` +> Like `class_reset`, but only considers services that were started + after /data was mounted. Only used for FDE devices. + `class_restart ` > Restarts all services of the specified class. @@ -494,6 +502,10 @@ Commands `write` command to write to `/proc/sys/kernel/printk` to change that. Properties are expanded within _level_. +`mark_post_data` +> Used to mark the point right after /data is mounted. Used to implement the + `class_reset_post_data` and `class_start_post_data` commands. + `mkdir [mode] [owner] [group]` > Create a directory at _path_, optionally with the given mode, owner, and group. If not provided, the directory is created with permissions 755 and diff --git a/init/builtins.cpp b/init/builtins.cpp index 06da4be9a..8bd2718ad 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -104,23 +104,37 @@ static void ForEachServiceInClass(const std::string& classname, F function) { } } -static Result do_class_start(const BuiltinArguments& args) { +static Result class_start(const std::string& class_name, bool post_data_only) { // Do not start a class if it has a property persist.dont_start_class.CLASS set to 1. - if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false)) + if (android::base::GetBoolProperty("persist.init.dont_start_class." + class_name, false)) return Success(); // Starting a class does not start services which are explicitly disabled. // They must be started individually. for (const auto& service : ServiceList::GetInstance()) { - if (service->classnames().count(args[1])) { + if (service->classnames().count(class_name)) { + if (post_data_only && !service->is_post_data()) { + continue; + } if (auto result = service->StartIfNotDisabled(); !result) { LOG(ERROR) << "Could not start service '" << service->name() - << "' as part of class '" << args[1] << "': " << result.error(); + << "' as part of class '" << class_name << "': " << result.error(); } } } return Success(); } +static Result do_class_start(const BuiltinArguments& args) { + return class_start(args[1], false /* post_data_only */); +} + +static Result do_class_start_post_data(const BuiltinArguments& args) { + if (args.context != kInitContext) { + return Error() << "command 'class_start_post_data' only available in init context"; + } + return class_start(args[1], true /* post_data_only */); +} + static Result do_class_stop(const BuiltinArguments& args) { ForEachServiceInClass(args[1], &Service::Stop); return Success(); @@ -131,6 +145,14 @@ static Result do_class_reset(const BuiltinArguments& args) { return Success(); } +static Result do_class_reset_post_data(const BuiltinArguments& args) { + if (args.context != kInitContext) { + return Error() << "command 'class_reset_post_data' only available in init context"; + } + ForEachServiceInClass(args[1], &Service::ResetIfPostData); + return Success(); +} + static Result do_class_restart(const BuiltinArguments& args) { // Do not restart a class if it has a property persist.dont_start_class.CLASS set to 1. if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false)) @@ -1053,6 +1075,12 @@ static Result do_init_user0(const BuiltinArguments& args) { {{"exec", "/system/bin/vdc", "--wait", "cryptfs", "init_user0"}, args.context}); } +static Result do_mark_post_data(const BuiltinArguments& args) { + ServiceList::GetInstance().MarkPostData(); + + return Success(); +} + static Result do_parse_apex_configs(const BuiltinArguments& args) { glob_t glob_result; // @ is added to filter out the later paths, which are bind mounts of the places @@ -1104,8 +1132,10 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"chmod", {2, 2, {true, do_chmod}}}, {"chown", {2, 3, {true, do_chown}}}, {"class_reset", {1, 1, {false, do_class_reset}}}, + {"class_reset_post_data", {1, 1, {false, do_class_reset_post_data}}}, {"class_restart", {1, 1, {false, do_class_restart}}}, {"class_start", {1, 1, {false, do_class_start}}}, + {"class_start_post_data", {1, 1, {false, do_class_start_post_data}}}, {"class_stop", {1, 1, {false, do_class_stop}}}, {"copy", {2, 2, {true, do_copy}}}, {"domainname", {1, 1, {true, do_domainname}}}, @@ -1125,6 +1155,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"load_persist_props", {0, 0, {false, do_load_persist_props}}}, {"load_system_props", {0, 0, {false, do_load_system_props}}}, {"loglevel", {1, 1, {false, do_loglevel}}}, + {"mark_post_data", {0, 0, {false, do_mark_post_data}}}, {"mkdir", {1, 4, {true, do_mkdir}}}, // TODO: Do mount operations in vendor_init. // mount_all is currently too complex to run in vendor_init as it queues action triggers, diff --git a/init/service.cpp b/init/service.cpp index f5c13b983..8c3e228a5 100644 --- a/init/service.cpp +++ b/init/service.cpp @@ -362,7 +362,7 @@ void Service::Reap(const siginfo_t& siginfo) { // Oneshot processes go into the disabled state on exit, // except when manually restarted. - if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART)) { + if ((flags_ & SVC_ONESHOT) && !(flags_ & SVC_RESTART) && !(flags_ & SVC_RESET)) { flags_ |= SVC_DISABLED; } @@ -947,6 +947,8 @@ Result Service::Start() { pre_apexd_ = true; } + post_data_ = ServiceList::GetInstance().IsPostData(); + LOG(INFO) << "starting service '" << name_ << "'..."; pid_t pid = -1; @@ -1146,6 +1148,12 @@ void Service::Reset() { StopOrReset(SVC_RESET); } +void Service::ResetIfPostData() { + if (post_data_) { + StopOrReset(SVC_RESET); + } +} + void Service::Stop() { StopOrReset(SVC_DISABLED); } @@ -1339,6 +1347,14 @@ void ServiceList::DumpState() const { } } +void ServiceList::MarkPostData() { + post_data_ = true; +} + +bool ServiceList::IsPostData() { + return post_data_; +} + void ServiceList::MarkServicesUpdate() { services_update_finished_ = true; diff --git a/init/service.h b/init/service.h index c42a5a371..dc2b12883 100644 --- a/init/service.h +++ b/init/service.h @@ -81,6 +81,7 @@ class Service { Result StartIfNotDisabled(); Result Enable(); void Reset(); + void ResetIfPostData(); void Stop(); void Terminate(); void Timeout(); @@ -124,6 +125,7 @@ class Service { std::optional timeout_period() const { return timeout_period_; } const std::vector& args() const { return args_; } bool is_updatable() const { return updatable_; } + bool is_post_data() const { return post_data_; } private: using OptionParser = Result (Service::*)(std::vector&& args); @@ -244,6 +246,8 @@ class Service { std::vector> reap_callbacks_; bool pre_apexd_ = false; + + bool post_data_ = false; }; class ServiceList { @@ -285,6 +289,8 @@ class ServiceList { const std::vector>& services() const { return services_; } const std::vector services_in_shutdown_order() const; + void MarkPostData(); + bool IsPostData(); void MarkServicesUpdate(); bool IsServicesUpdated() const { return services_update_finished_; } void DelayService(const Service& service); @@ -292,6 +298,7 @@ class ServiceList { private: std::vector> services_; + bool post_data_ = false; bool services_update_finished_ = false; std::vector delayed_service_names_; }; diff --git a/rootdir/init.rc b/rootdir/init.rc index 295e70479..bd3ef45c9 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -405,6 +405,8 @@ on late-fs class_start early_hal on post-fs-data + mark_post_data + # Start checkpoint before we touch data start vold exec - system system -- /system/bin/vdc checkpoint prepareCheckpoint @@ -747,9 +749,6 @@ on property:sys.init_log_level=* on charger class_start charger -on property:vold.decrypt=trigger_reset_main - class_reset main - on property:vold.decrypt=trigger_load_persist_props load_persist_props start logd @@ -767,6 +766,8 @@ on property:vold.decrypt=trigger_restart_min_framework on property:vold.decrypt=trigger_restart_framework # A/B update verifier that marks a successful boot. exec_start update_verifier + class_start_post_data hal + class_start_post_data core class_start main class_start late_start setprop service.bootanim.exit 0 @@ -775,6 +776,8 @@ on property:vold.decrypt=trigger_restart_framework on property:vold.decrypt=trigger_shutdown_framework class_reset late_start class_reset main + class_reset_post_data core + class_reset_post_data hal on property:sys.boot_completed=1 bootchart stop