From 59656fb37769b3c0de927c20f8e9f5855a0f7ac3 Mon Sep 17 00:00:00 2001 From: Tom Cherry Date: Tue, 28 May 2019 10:19:44 -0700 Subject: [PATCH] init: dump stack when aborting Dump init stacks when aborting either due to LOG(FATAL) or in userdebug/eng builds due to signals, including signals from sanitizers. Doesn't work for static first stage init yet, b/133450393 tracks that. Also, ensure that LOG(FATAL) in child processes calls abort() in all stages of init, not just 2nd stage init. Bug: 131747478 Test: abort init in various ways and see stacks Test: hang or crash in backtrace handler and see child reboot Change-Id: Ib53b5d3e7e814244203f875de016ada9900dfce8 --- init/Android.bp | 1 + init/Android.mk | 4 ++++ init/first_stage_init.cpp | 8 ++----- init/host_init_stubs.h | 5 +++++ init/init.cpp | 16 ++------------ init/reboot_utils.cpp | 37 +++++++++++++++++++++++++++++--- init/reboot_utils.h | 1 + init/selinux.cpp | 5 +---- init/util.cpp | 44 ++++++++++++++++++++++++++++++++------- init/util.h | 3 ++- 10 files changed, 89 insertions(+), 35 deletions(-) diff --git a/init/Android.bp b/init/Android.bp index 383a69dc8..f71437f43 100644 --- a/init/Android.bp +++ b/init/Android.bp @@ -68,6 +68,7 @@ cc_defaults { "libpropertyinfoparser", ], shared_libs: [ + "libbacktrace", "libbase", "libbinder", "libbootloader_message", diff --git a/init/Android.mk b/init/Android.mk index b02c926da..c4f7d34b2 100644 --- a/init/Android.mk +++ b/init/Android.mk @@ -105,6 +105,10 @@ LOCAL_STATIC_LIBRARIES := \ libcap \ libgsi \ libcom.android.sysprop.apex \ + liblzma \ + libdexfile_support \ + libunwindstack \ + libbacktrace \ LOCAL_SANITIZE := signed-integer-overflow # First stage init is weird: it may start without stdout/stderr, and no /proc. diff --git a/init/first_stage_init.cpp b/init/first_stage_init.cpp index 7dd3ad46d..5d64f415d 100644 --- a/init/first_stage_init.cpp +++ b/init/first_stage_init.cpp @@ -33,7 +33,6 @@ #include #include #include -#include #include #include "debug_ramdisk.h" @@ -168,13 +167,10 @@ int FirstStageMain(int argc, char** argv) { "mode=0755,uid=0,gid=0")); #undef CHECKCALL + SetStdioToDevNull(argv); // Now that tmpfs is mounted on /dev and we have /dev/kmsg, we can actually // talk to the outside world... - // We need to set up stdin/stdout/stderr for child processes forked from first - // stage init as part of the mount process. This closes /dev/console if the - // kernel had previously opened it. - auto reboot_bootloader = [](const char*) { RebootSystem(ANDROID_RB_RESTART2, "bootloader"); }; - InitKernelLogging(argv, reboot_bootloader); + InitKernelLogging(argv); if (!errors.empty()) { for (const auto& [error_string, error_errno] : errors) { diff --git a/init/host_init_stubs.h b/init/host_init_stubs.h index 63ceead67..a711340fa 100644 --- a/init/host_init_stubs.h +++ b/init/host_init_stubs.h @@ -44,6 +44,11 @@ extern uint32_t (*property_set)(const std::string& name, const std::string& valu uint32_t HandlePropertySet(const std::string& name, const std::string& value, const std::string& source_context, const ucred& cr, std::string* error); +// reboot_utils.h +inline void __attribute__((noreturn)) InitFatalReboot() { + abort(); +} + // selinux.h int SelinuxGetVendorAndroidVersion(); void SelabelInitialize(); diff --git a/init/init.cpp b/init/init.cpp index 0615d4479..548e8c577 100644 --- a/init/init.cpp +++ b/init/init.cpp @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include @@ -605,17 +604,6 @@ void HandleKeychord(const std::vector& keycodes) { } } -static void InitAborter(const char* abort_message) { - // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to - // simply abort instead of trying to reboot the system. - if (getpid() != 1) { - android::base::DefaultAborter(abort_message); - return; - } - - RebootSystem(ANDROID_RB_RESTART2, "bootloader"); -} - static void GlobalSeccomp() { import_kernel_cmdline(false, [](const std::string& key, const std::string& value, bool in_qemu) { @@ -663,8 +651,8 @@ int SecondStageMain(int argc, char** argv) { boot_clock::time_point start_time = boot_clock::now(); - // We need to set up stdin/stdout/stderr again now that we're running in init's context. - InitKernelLogging(argv, InitAborter); + SetStdioToDevNull(argv); + InitKernelLogging(argv); LOG(INFO) << "init second stage started!"; // Set init and its forked children's oom_adj. diff --git a/init/reboot_utils.cpp b/init/reboot_utils.cpp index 961030409..4524f6983 100644 --- a/init/reboot_utils.cpp +++ b/init/reboot_utils.cpp @@ -19,8 +19,13 @@ #include #include -#include -#include +#include + +#include "android-base/file.h" +#include "android-base/logging.h" +#include "android-base/strings.h" +#include "backtrace/Backtrace.h" +#include "cutils/android_reboot.h" #include "capabilities.h" @@ -75,6 +80,32 @@ void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& abort(); } +void __attribute__((noreturn)) InitFatalReboot() { + auto pid = fork(); + + if (pid == -1) { + // Couldn't fork, don't even try to backtrace, just reboot. + RebootSystem(ANDROID_RB_RESTART2, "bootloader"); + } else if (pid == 0) { + // Fork a child for safety, since we always want to shut down if something goes wrong, but + // its worth trying to get the backtrace, even in the signal handler, since typically it + // does work despite not being async-signal-safe. + sleep(5); + RebootSystem(ANDROID_RB_RESTART2, "bootloader"); + } + + // In the parent, let's try to get a backtrace then shutdown. + std::unique_ptr backtrace( + Backtrace::Create(BACKTRACE_CURRENT_PROCESS, BACKTRACE_CURRENT_THREAD)); + if (!backtrace->Unwind(0)) { + LOG(ERROR) << __FUNCTION__ << ": Failed to unwind callstack."; + } + for (size_t i = 0; i < backtrace->NumFrames(); i++) { + LOG(ERROR) << backtrace->FormatFrameData(i); + } + RebootSystem(ANDROID_RB_RESTART2, "bootloader"); +} + void InstallRebootSignalHandlers() { // Instead of panic'ing the kernel as is the default behavior when init crashes, // we prefer to reboot to bootloader on development builds, as this will prevent @@ -94,7 +125,7 @@ void InstallRebootSignalHandlers() { // RebootSystem uses syscall() which isn't actually async-signal-safe, but our only option // and probably good enough given this is already an error case and only enabled for // development builds. - RebootSystem(ANDROID_RB_RESTART2, "bootloader"); + InitFatalReboot(); }; action.sa_flags = SA_RESTART; sigaction(SIGABRT, &action, nullptr); diff --git a/init/reboot_utils.h b/init/reboot_utils.h index 073a16a68..c4d97d57b 100644 --- a/init/reboot_utils.h +++ b/init/reboot_utils.h @@ -26,6 +26,7 @@ namespace init { bool IsRebootCapable(); // This is a wrapper around the actual reboot calls. void __attribute__((noreturn)) RebootSystem(unsigned int cmd, const std::string& reboot_target); +void __attribute__((noreturn)) InitFatalReboot(); void InstallRebootSignalHandlers(); } // namespace init diff --git a/init/selinux.cpp b/init/selinux.cpp index 8a63363ed..8a9662b6f 100644 --- a/init/selinux.cpp +++ b/init/selinux.cpp @@ -60,7 +60,6 @@ #include #include #include -#include #include #include @@ -522,9 +521,7 @@ int SelinuxGetVendorAndroidVersion() { // 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"); - }); + InitKernelLogging(argv); if (REBOOT_BOOTLOADER_ON_PANIC) { InstallRebootSignalHandlers(); diff --git a/init/util.cpp b/init/util.cpp index 29d7a7698..790ab927a 100644 --- a/init/util.cpp +++ b/init/util.cpp @@ -40,6 +40,7 @@ #include #if defined(__ANDROID__) +#include "reboot_utils.h" #include "selinux.h" #else #include "host_init_stubs.h" @@ -425,20 +426,49 @@ bool IsLegalPropertyName(const std::string& name) { return true; } -void InitKernelLogging(char** argv, std::function abort_function) { +static void InitAborter(const char* abort_message) { + // When init forks, it continues to use this aborter for LOG(FATAL), but we want children to + // simply abort instead of trying to reboot the system. + if (getpid() != 1) { + android::base::DefaultAborter(abort_message); + return; + } + + InitFatalReboot(); +} + +// The kernel opens /dev/console and uses that fd for stdin/stdout/stderr if there is a serial +// console enabled and no initramfs, otherwise it does not provide any fds for stdin/stdout/stderr. +// SetStdioToDevNull() is used to close these existing fds if they exist and replace them with +// /dev/null regardless. +// +// In the case that these fds are provided by the kernel, the exec of second stage init causes an +// SELinux denial as it does not have access to /dev/console. In the case that they are not +// provided, exec of any further process is potentially dangerous as the first fd's opened by that +// process will take the stdin/stdout/stderr fileno's, which can cause issues if printf(), etc is +// then used by that process. +// +// Lastly, simply calling SetStdioToDevNull() in first stage init is not enough, since first +// stage init still runs in kernel context, future child processes will not have permissions to +// access any fds that it opens, including the one opened below for /dev/null. Therefore, +// SetStdioToDevNull() must be called again in second stage init. +void SetStdioToDevNull(char** argv) { // Make stdin/stdout/stderr all point to /dev/null. int fd = open("/dev/null", O_RDWR); if (fd == -1) { int saved_errno = errno; - android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function)); + android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter); errno = saved_errno; PLOG(FATAL) << "Couldn't open /dev/null"; } - dup2(fd, 0); - dup2(fd, 1); - dup2(fd, 2); - if (fd > 2) close(fd); - android::base::InitLogging(argv, &android::base::KernelLogger, std::move(abort_function)); + dup2(fd, STDIN_FILENO); + dup2(fd, STDOUT_FILENO); + dup2(fd, STDERR_FILENO); + if (fd > STDERR_FILENO) close(fd); +} + +void InitKernelLogging(char** argv) { + android::base::InitLogging(argv, &android::base::KernelLogger, InitAborter); } bool IsRecoveryMode() { diff --git a/init/util.h b/init/util.h index 2232a0f0b..767620b16 100644 --- a/init/util.h +++ b/init/util.h @@ -63,7 +63,8 @@ bool is_android_dt_value_expected(const std::string& sub_path, const std::string bool IsLegalPropertyName(const std::string& name); -void InitKernelLogging(char** argv, std::function abort_function); +void SetStdioToDevNull(char** argv); +void InitKernelLogging(char** argv); bool IsRecoveryMode(); } // namespace init } // namespace android