From 7bfea3d59cdcd275be73bc4eba343ee6a5d3b06b Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Tue, 6 Nov 2018 14:12:05 -0800 Subject: [PATCH] init: clean up the 1st/2nd stage init split The first split of 1st/2nd stage init went a bit overboard, since it split these even in the case of the recovery image and system-as-root, which don't actually need the split. This change simplifies this a bit: system-as-root and recovery have a single combined /system/bin/init and a symlink from /init to it. non-system-as-root has a separate first stage init at /init on the first stage ramdisk and a combined /system/bin/init on system.img. Two particular benefits from this: 1) Removal of the rsync of TARGET_RAMDISK_OUT to the recovery image 2) Decrease of overall space on the recovery image since it won't have a statically linked first stage init This also unified the various entry points of init to depend entirely on the arguments passed to it, instead of the hybrid of arguments and environment variable used previously. Bug: 80395578 Test: boot both system-as-root and non-system-as-root Change-Id: Ic2f29b6f56b7defc80eaa0e7cd0c9107e978816f --- init/Android.bp | 3 + init/Android.mk | 20 +++-- ...t_first_stage.cpp => first_stage_init.cpp} | 10 +-- init/first_stage_init.h | 25 +++++++ init/first_stage_main.cpp | 21 ++++++ init/init.cpp | 73 +------------------ init/init.h | 2 +- init/main.cpp | 64 +++++++++++++++- init/selinux.cpp | 43 ++++++++++- init/selinux.h | 2 +- 10 files changed, 167 insertions(+), 96 deletions(-) rename init/{init_first_stage.cpp => first_stage_init.cpp} (98%) create mode 100644 init/first_stage_init.h create mode 100644 init/first_stage_main.cpp diff --git a/init/Android.bp b/init/Android.bp index ff3b61fe2..d82ec66c0 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -79,6 +79,7 @@ cc_defaults { "libkeyutils", "liblog", "liblogwrap", + "liblp", "libselinux", "libutils", ], @@ -99,6 +100,7 @@ cc_library_static { "devices.cpp", "epoll.cpp", "firmware_handler.cpp", + "first_stage_init.cpp", "first_stage_mount.cpp", "import_parser.cpp", "init.cpp", @@ -117,6 +119,7 @@ cc_library_static { "sigchld_handler.cpp", "subcontext.cpp", "subcontext.proto", + "switch_root.cpp", "rlimit_parser.cpp", "tokenizer.cpp", "uevent_listener.cpp", diff --git a/init/Android.mk b/init/Android.mk index dc46d21c5..700c81e39 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -39,12 +39,15 @@ init_cflags += \ # -- +# Do not build this even with mmma if we're system-as-root, otherwise it will overwrite the symlink. +ifneq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) include $(CLEAR_VARS) LOCAL_CPPFLAGS := $(init_cflags) LOCAL_SRC_FILES := \ devices.cpp \ + first_stage_init.cpp \ + first_stage_main.cpp \ first_stage_mount.cpp \ - init_first_stage.cpp \ reboot_utils.cpp \ selinux.cpp \ switch_root.cpp \ @@ -93,19 +96,16 @@ LOCAL_SANITIZE := signed-integer-overflow # First stage init is weird: it may start without stdout/stderr, and no /proc. LOCAL_NOSANITIZE := hwaddress include $(BUILD_EXECUTABLE) +endif include $(CLEAR_VARS) LOCAL_MODULE := init_system +LOCAL_REQUIRED_MODULES := \ + init_second_stage \ + ifeq ($(BOARD_BUILD_SYSTEM_ROOT_IMAGE),true) -LOCAL_REQUIRED_MODULES := \ - init_first_stage \ - init_second_stage \ - -else -LOCAL_REQUIRED_MODULES := \ - init_second_stage \ - +LOCAL_POST_INSTALL_CMD := ln -sf /system/bin/init $(TARGET_ROOT_OUT)/init endif include $(BUILD_PHONY_PACKAGE) @@ -118,5 +118,3 @@ LOCAL_REQUIRED_MODULES := \ endif include $(BUILD_PHONY_PACKAGE) - - diff --git a/init/init_first_stage.cpp b/init/first_stage_init.cpp similarity index 98% rename from init/init_first_stage.cpp rename to init/first_stage_init.cpp index c2c686885..e11d89768 100644 --- a/init/init_first_stage.cpp +++ b/init/first_stage_init.cpp @@ -14,6 +14,8 @@ * limitations under the License. */ +#include "first_stage_init.h" + #include #include #include @@ -94,7 +96,7 @@ bool ForceNormalBoot() { } // namespace -int main(int argc, char** argv) { +int FirstStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } @@ -214,7 +216,7 @@ int main(int argc, char** argv) { setenv("INIT_STARTED_AT", std::to_string(start_ms).c_str(), 1); const char* path = "/system/bin/init"; - const char* args[] = {path, nullptr}; + const char* args[] = {path, "selinux_setup", nullptr}; execv(path, const_cast(args)); // execv() only returns if an error happened, in which case we @@ -226,7 +228,3 @@ int main(int argc, char** argv) { } // namespace init } // namespace android - -int main(int argc, char** argv) { - return android::init::main(argc, argv); -} diff --git a/init/first_stage_init.h b/init/first_stage_init.h new file mode 100644 index 000000000..0476e4431 --- /dev/null +++ b/init/first_stage_init.h @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2018 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. + */ + +#pragma once + +namespace android { +namespace init { + +int FirstStageMain(int argc, char** argv); + +} // namespace init +} // namespace android diff --git a/init/first_stage_main.cpp b/init/first_stage_main.cpp new file mode 100644 index 000000000..7bae84cae --- /dev/null +++ b/init/first_stage_main.cpp @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018 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 "first_stage_init.h" + +int main(int argc, char** argv) { + return android::init::FirstStageMain(argc, argv); +} diff --git a/init/init.cpp b/init/init.cpp index e7dbc1197..39c18327a 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -59,13 +59,8 @@ #include "security.h" #include "selinux.h" #include "sigchld_handler.h" -#include "ueventd.h" #include "util.h" -#if __has_feature(address_sanitizer) -#include -#endif - using namespace std::chrono_literals; using namespace std::string_literals; @@ -79,25 +74,6 @@ using android::base::Trim; namespace android { namespace init { -#if __has_feature(address_sanitizer) -// Load asan.options if it exists since these are not yet in the environment. -// Always ensure detect_container_overflow=0 as there are false positives with this check. -// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds. -extern "C" const char* __asan_default_options() { - return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1"; -} - -__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void -__sanitizer_report_error_summary(const char* summary) { - LOG(ERROR) << "Main stage (error summary): " << summary; -} - -__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void -AsanReportCallback(const char* str) { - LOG(ERROR) << "Main stage: " << str; -} -#endif - static int property_triggers_enabled = 0; static char qemu[32]; @@ -622,57 +598,11 @@ static void GlobalSeccomp() { }); } -static void SetupSelinux(char** argv) { - android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) { - RebootSystem(ANDROID_RB_RESTART2, "bootloader"); - }); - - // Set up SELinux, loading the SELinux policy. - SelinuxSetupKernelLogging(); - SelinuxInitialize(); - - // We're in the kernel domain and want to transition to the init domain. File systems that - // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here, - // but other file systems do. In particular, this is needed for ramdisks such as the - // recovery image for A/B devices. - if (selinux_android_restorecon("/system/bin/init", 0) == -1) { - PLOG(FATAL) << "restorecon failed of /system/bin/init failed"; - } - - setenv("SELINUX_INITIALIZED", "true", 1); - - const char* path = "/system/bin/init"; - const char* args[] = {path, nullptr}; - execv(path, const_cast(args)); - - // execv() only returns if an error happened, in which case we - // panic and never return from this function. - PLOG(FATAL) << "execv(\"" << path << "\") failed"; -} - -int main(int argc, char** argv) { -#if __has_feature(address_sanitizer) - __asan_set_error_report_callback(AsanReportCallback); -#endif - - if (!strcmp(basename(argv[0]), "ueventd")) { - return ueventd_main(argc, argv); - } - - if (argc > 1 && !strcmp(argv[1], "subcontext")) { - android::base::InitLogging(argv, &android::base::KernelLogger); - const BuiltinFunctionMap function_map; - return SubcontextMain(argc, argv, &function_map); - } - +int SecondStageMain(int argc, char** argv) { if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); } - if (getenv("SELINUX_INITIALIZED") == nullptr) { - SetupSelinux(argv); - } - // We need to set up stdin/stdout/stderr again now that we're running in init's context. InitKernelLogging(argv, InitAborter); LOG(INFO) << "init second stage started!"; @@ -708,7 +638,6 @@ int main(int argc, char** argv) { if (avb_version) property_set("ro.boot.avb_version", avb_version); // Clean up our environment. - unsetenv("SELINUX_INITIALIZED"); unsetenv("INIT_STARTED_AT"); unsetenv("INIT_SELINUX_TOOK"); unsetenv("INIT_AVB_VERSION"); diff --git a/init/init.h b/init/init.h index 65405aaeb..a76da2014 100644 --- a/init/init.h +++ b/init/init.h @@ -50,7 +50,7 @@ void DumpState(); void ResetWaitForProp(); -int main(int argc, char** argv); +int SecondStageMain(int argc, char** argv); } // namespace init } // namespace android diff --git a/init/main.cpp b/init/main.cpp index 9ed451b16..868c409d0 100644 --- a/init/main.cpp +++ b/init/main.cpp @@ -14,8 +14,70 @@ * limitations under the License. */ +#include "builtins.h" +#include "first_stage_init.h" #include "init.h" +#include "selinux.h" +#include "subcontext.h" +#include "ueventd.h" + +#include + +#if __has_feature(address_sanitizer) +#include +#endif + +#if __has_feature(address_sanitizer) +// Load asan.options if it exists since these are not yet in the environment. +// Always ensure detect_container_overflow=0 as there are false positives with this check. +// Always ensure abort_on_error=1 to ensure we reboot to bootloader for development builds. +extern "C" const char* __asan_default_options() { + return "include_if_exists=/system/asan.options:detect_container_overflow=0:abort_on_error=1"; +} + +__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) extern "C" void +__sanitizer_report_error_summary(const char* summary) { + LOG(ERROR) << "Init (error summary): " << summary; +} + +__attribute__((no_sanitize("address", "memory", "thread", "undefined"))) static void +AsanReportCallback(const char* str) { + LOG(ERROR) << "Init: " << str; +} +#endif + +using namespace android::init; int main(int argc, char** argv) { - android::init::main(argc, argv); +#if __has_feature(address_sanitizer) + __asan_set_error_report_callback(AsanReportCallback); +#endif + + if (!strcmp(basename(argv[0]), "ueventd")) { + return ueventd_main(argc, argv); + } + + if (argc < 2) { + return FirstStageMain(argc, argv); + } + + if (!strcmp(argv[1], "subcontext")) { + android::base::InitLogging(argv, &android::base::KernelLogger); + const BuiltinFunctionMap function_map; + + return SubcontextMain(argc, argv, &function_map); + } + + if (!strcmp(argv[1], "selinux_setup")) { + return SetupSelinux(argv); + } + + if (!strcmp(argv[1], "second_stage")) { + return SecondStageMain(argc, argv); + } + + android::base::InitLogging(argv, &android::base::KernelLogger); + + LOG(ERROR) << "Unknown argument passed to init '" << argv[1] << "'"; + return 1; } diff --git a/init/selinux.cpp b/init/selinux.cpp index fd7e86f8a..3a0909664 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp @@ -18,8 +18,8 @@ // for SELinux operation for init. // When the system boots, there is no SEPolicy present and init is running in the kernel domain. -// Init loads the SEPolicy from the file system, restores the context of /init based on this -// SEPolicy, and finally exec()'s itself to run in the proper domain. +// Init loads the SEPolicy from the file system, restores the context of /system/bin/init based on +// this SEPolicy, and finally exec()'s itself to run in the proper domain. // The SEPolicy on Android comes in two variants: monolithic and split. @@ -58,8 +58,10 @@ #include #include #include +#include #include +#include "reboot_utils.h" #include "util.h" using android::base::ParseInt; @@ -379,8 +381,6 @@ bool LoadPolicy() { return IsSplitPolicyDevice() ? LoadSplitPolicy() : LoadMonolithicPolicy(); } -} // namespace - void SelinuxInitialize() { Timer t; @@ -405,6 +405,8 @@ void SelinuxInitialize() { setenv("INIT_SELINUX_TOOK", std::to_string(t.duration().count()).c_str(), 1); } +} // namespace + // The files and directories that were created before initial sepolicy load or // files on ramdisk need to have their security context restored to the proper // value. This must happen before /dev is populated by ueventd. @@ -496,6 +498,39 @@ int SelinuxGetVendorAndroidVersion() { return major_version; } +// This function initializes SELinux then execs init to run in the init SELinux context. +int SetupSelinux(char** argv) { + android::base::InitLogging(argv, &android::base::KernelLogger, [](const char*) { + RebootSystem(ANDROID_RB_RESTART2, "bootloader"); + }); + + if (REBOOT_BOOTLOADER_ON_PANIC) { + InstallRebootSignalHandlers(); + } + + // Set up SELinux, loading the SELinux policy. + SelinuxSetupKernelLogging(); + SelinuxInitialize(); + + // We're in the kernel domain and want to transition to the init domain. File systems that + // store SELabels in their xattrs, such as ext4 do not need an explicit restorecon here, + // but other file systems do. In particular, this is needed for ramdisks such as the + // recovery image for A/B devices. + if (selinux_android_restorecon("/system/bin/init", 0) == -1) { + PLOG(FATAL) << "restorecon failed of /system/bin/init failed"; + } + + const char* path = "/system/bin/init"; + const char* args[] = {path, "second_stage", nullptr}; + execv(path, const_cast(args)); + + // execv() only returns if an error happened, in which case we + // panic and never return from this function. + PLOG(FATAL) << "execv(\"" << path << "\") failed"; + + return 1; +} + // selinux_android_file_context_handle() takes on the order of 10+ms to run, so we want to cache // its value. selinux_android_restorecon() also needs an sehandle for file context look up. It // will create and store its own copy, but selinux_android_set_sehandle() can be used to provide diff --git a/init/selinux.h b/init/selinux.h index c41d7f0d9..3aa94063e 100644 --- a/init/selinux.h +++ b/init/selinux.h @@ -23,7 +23,7 @@ namespace android { namespace init { -void SelinuxInitialize(); +int SetupSelinux(char** argv); void SelinuxRestoreContext(); void SelinuxSetupKernelLogging();