From 4ba548d845c0306a46d7cc8cf483caac5756159c Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Fri, 22 Feb 2019 16:04:35 +0900 Subject: [PATCH 1/2] mount /apex during first_stage init /apex is not mounted via init.rc but directly by the first_stage init before the mount namespaces are configured. This allows us to change the propagation type for /apex mount point to private to isolate APEX activatesions across post- and pre-apexd processes. Bug: 125549215 Test: m; device boots to the UI Change-Id: I10e056cd30d64cb702b6c237acd8dab326162884 --- init/first_stage_init.cpp | 4 ++++ init/selinux.cpp | 2 ++ rootdir/init.rc | 6 ------ 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp index e11d89768..7cf4c3fbd 100644 --- a/init/first_stage_init.cpp +++ b/init/first_stage_init.cpp @@ -155,6 +155,10 @@ int FirstStageMain(int argc, char** argv) { // part of the product partition, e.g. because they are mounted read-write. CHECKCALL(mkdir("/mnt/product", 0755)); + // /apex is used to mount APEXes + CHECKCALL(mount("tmpfs", "/apex", "tmpfs", MS_NOEXEC | MS_NOSUID | MS_NODEV, + "mode=0755,uid=0,gid=0")); + #undef CHECKCALL // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually diff --git a/init/selinux.cpp b/init/selinux.cpp index ee302c168..3fadfedbe 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp @@ -459,6 +459,8 @@ void SelinuxRestoreContext() { selinux_android_restorecon("/dev/block", SELINUX_ANDROID_RESTORECON_RECURSE); selinux_android_restorecon("/dev/device-mapper", 0); + + selinux_android_restorecon("/apex", 0); } int SelinuxKlogCallback(int type, const char* fmt, ...) { diff --git a/rootdir/init.rc b/rootdir/init.rc index ce4b380f2..52afec95a 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -278,12 +278,6 @@ on init write /dev/cpu_variant:${ro.bionic.2nd_arch} ${ro.bionic.2nd_cpu_variant} chmod 0444 /dev/cpu_variant:${ro.bionic.2nd_arch} - # Setup APEX mount point and its security context - mount tmpfs tmpfs /apex nodev noexec nosuid - chmod 0755 /apex - chown root root /apex - restorecon /apex - # Start logd before any other services run to ensure we capture all of their logs. start logd From dcbaf9f41b94cc5ddd12523a48b7be98704332ff Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Fri, 22 Feb 2019 22:15:25 +0900 Subject: [PATCH 2/2] Activate system APEXes early Summary: Boot sequence around apexd is changed to make it possible for pre-apexd processes to use libraries from APEXes. They no longer need to wait for the apexd to finish activating APEXes, which again can be done only after /data/ is mounted. This improves overall boot performance. Detail: This change fixes the problem that processes that are started before apexd (so called pre-apexd processes) can't access libraries that are provided only by the APEXes but are not found in the system partition (e.g. libdexfile_external.so, etc.). Main idea is to activate system APEXes (/system/apex/*.apex) before /data is mounted and then activate the updated APEXes (/data/apex/*.apex) after the /data mount. Detailed boot sequence is as follows. 1) init prepares the bootstrap and default mount namespaces. A tmpfs is mounted on /apex and the propagation type of the mountpoint is set to private. 2) before any other process is started, apexd is started in bootstrap mode. When executed in the mode, apexd only activates APEXes under /system/apex. Note that APEXes activated in this phase are mounted in the bootstrap mount namespace only. 3) other pre-apexd processes are started. They are in the bootstrap mount namespace and thus are provided with the libraries from the system APEXes. 4) /data is mounted. init switches into the default mount namespace and starts apexd as a daemon as usual. 5) apexd scans both /data/apex and /system/apex, and activate latest APEXes from the directories. Note that APEXes activated in this phase are mounted in the default namespaces only and thus are not visible to the pre-apexd processes. Bug: 125549215 Test: m; device boots Change-Id: I21c60d0ebe188fa4f24d6e6861f85ca204843069 --- init/builtins.cpp | 11 +++++++++- init/mount_namespace.cpp | 18 +++++++++++++++- init/mount_namespace.h | 1 + rootdir/init.rc | 46 ++++++++++++++++++---------------------- 4 files changed, 49 insertions(+), 27 deletions(-) diff --git a/init/builtins.cpp b/init/builtins.cpp index 538ed00eb..6511d299e 100644 --- a/init/builtins.cpp +++ b/init/builtins.cpp @@ -1119,13 +1119,21 @@ static Result do_parse_apex_configs(const BuiltinArguments& args) { } static Result do_setup_runtime_bionic(const BuiltinArguments& args) { - if (SwitchToDefaultMountNamespace()) { + if (SetupRuntimeBionic()) { return Success(); } else { return Error() << "Failed to setup runtime bionic"; } } +static Result do_enter_default_mount_ns(const BuiltinArguments& args) { + if (SwitchToDefaultMountNamespace()) { + return Success(); + } else { + return Error() << "Failed to enter into default mount namespace"; + } +} + // Builtin-function-map start const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { constexpr std::size_t kMax = std::numeric_limits::max(); @@ -1177,6 +1185,7 @@ const BuiltinFunctionMap::Map& BuiltinFunctionMap::map() const { {"start", {1, 1, {false, do_start}}}, {"stop", {1, 1, {false, do_stop}}}, {"swapon_all", {1, 1, {false, do_swapon_all}}}, + {"enter_default_mount_ns", {0, 0, {false, do_enter_default_mount_ns}}}, {"symlink", {2, 2, {true, do_symlink}}}, {"sysclktz", {1, 1, {false, do_sysclktz}}}, {"trigger", {1, 1, {false, do_trigger}}}, diff --git a/init/mount_namespace.cpp b/init/mount_namespace.cpp index 413fe8f50..327446a30 100644 --- a/init/mount_namespace.cpp +++ b/init/mount_namespace.cpp @@ -172,6 +172,11 @@ bool SetupMountNamespaces() { kBionicLibsMountPointDir64)) return false; + // /apex is also a private mountpoint to give different sets of APEXes for + // the bootstrap and default mount namespaces. The processes running with + // the bootstrap namespace get APEXes from the read-only partition. + if (!(MakePrivate("/apex"))) return false; + bootstrap_ns_fd.reset(OpenMountNamespace()); bootstrap_ns_id = GetMountNamespaceId(); @@ -227,6 +232,17 @@ bool SwitchToDefaultMountNamespace() { } } + LOG(INFO) << "Switched to default mount namespace"; + return true; +} + +// TODO(jiyong): remove this when /system/lib/libc.so becomes +// a symlink to /apex/com.android.runtime/lib/bionic/libc.so +bool SetupRuntimeBionic() { + if (IsRecoveryMode()) { + // We don't have multiple namespaces in recovery mode + return true; + } // Bind-mount bionic from the runtime APEX since it is now available. Note // that in case of IsBionicUpdatable() == false, these mounts are over the // existing existing bind mounts for the bootstrap bionic, which effectively @@ -238,7 +254,7 @@ bool SwitchToDefaultMountNamespace() { kBionicLibsMountPointDir64)) return false; - LOG(INFO) << "Switched to default mount namespace"; + LOG(INFO) << "Runtime bionic is set up"; return true; } diff --git a/init/mount_namespace.h b/init/mount_namespace.h index c41a449f3..4eef7853e 100644 --- a/init/mount_namespace.h +++ b/init/mount_namespace.h @@ -20,6 +20,7 @@ namespace android { namespace init { bool SetupMountNamespaces(); +bool SetupRuntimeBionic(); bool SwitchToDefaultMountNamespace(); bool SwitchToBootstrapMountNamespaceIfNeeded(); diff --git a/rootdir/init.rc b/rootdir/init.rc index 52afec95a..b44cc3efa 100644 --- a/rootdir/init.rc +++ b/rootdir/init.rc @@ -13,12 +13,6 @@ import /init.${ro.zygote}.rc # Cgroups are mounted right before early-init using list from /etc/cgroups.json on early-init - # Mount shared so changes propagate into child namespaces - # Do this before other processes are started from init. Otherwise, - # processes launched while the propagation type of / is 'private' - # won't get mount events from others. - mount rootfs rootfs / shared rec - # Set init and its forked children's oom_adj. write /proc/1/oom_score_adj -1000 @@ -43,6 +37,11 @@ on early-init start ueventd + # Run apexd-bootstrap so that APEXes that provide critical libraries + # become available. Note that this is executed as exec_start to ensure that + # the libraries are available to the processes started after this statement. + exec_start apexd-bootstrap + on init sysclktz 0 @@ -281,9 +280,6 @@ on init # Start logd before any other services run to ensure we capture all of their logs. start logd - # Start apexd as soon as we can - start apexd - # Start essential services. start servicemanager start hwservicemanager @@ -419,8 +415,16 @@ on post-fs-data mkdir /data/bootchart 0755 shell shell bootchart start - # /data/apex is now available. Let apexd to scan and activate APEXes. - setprop apexd.data.status ready + # Make sure that apexd is started in the default namespace + enter_default_mount_ns + + # /data/apex is now available. Start apexd to scan and activate APEXes. + mkdir /data/apex 0750 root system + mkdir /data/apex/active 0750 root system + mkdir /data/apex/backup 0700 root system + mkdir /data/apex/sessions 0700 root system + mkdir /data/pkg_staging 0750 system system + start apexd # Avoid predictable entropy pool. Carry over entropy from previous boot. copy /data/system/entropy.dat /dev/urandom @@ -537,12 +541,6 @@ on post-fs-data mkdir /data/anr 0775 system system - mkdir /data/apex 0750 root system - mkdir /data/apex/active 0750 root system - mkdir /data/apex/backup 0700 root system - mkdir /data/apex/sessions 0700 root system - mkdir /data/pkg_staging 0750 system system - # NFC: create data/nfc for nv storage mkdir /data/nfc 0770 nfc nfc mkdir /data/nfc/param 0770 nfc nfc @@ -575,6 +573,12 @@ on post-fs-data mkdir /data/cache/backup_stage 0700 system system mkdir /data/cache/backup 0700 system system + # Wait for apexd to finish activating APEXes before starting more processes. + wait_for_prop apexd.status ready + # TODO(jiyong): remove setup_runtime_bionic + setup_runtime_bionic + parse_apex_configs + init_user0 # Set SELinux security contexts on upgrade or policy update. @@ -583,14 +587,6 @@ on post-fs-data # load fsverity keys exec -- /system/bin/mini-keyctl -c /product/etc/security/cacerts_fsverity,/vendor/etc/security/cacerts_fsverity -k .fs-verity - # Wait for apexd to finish activating APEXes before starting more processes. - # This certainly reduces the parallelism but is required to make as many processes - # as possible to use the bionic libs from the runtime APEX. This takes less than 50ms - # so the impact on the booting time is not significant. - wait_for_prop apexd.status ready - setup_runtime_bionic - parse_apex_configs - # Check any timezone data in /data is newer than the copy in the runtime module, delete if not. exec - system system -- /system/bin/tzdatacheck /apex/com.android.runtime/etc/tz /data/misc/zoneinfo