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();