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
This commit is contained in:
Tom Cherry 2019-05-28 10:19:44 -07:00
parent 5f7314b7e1
commit 59656fb377
10 changed files with 89 additions and 35 deletions

View file

@ -68,6 +68,7 @@ cc_defaults {
"libpropertyinfoparser",
],
shared_libs: [
"libbacktrace",
"libbase",
"libbinder",
"libbootloader_message",

View file

@ -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.

View file

@ -33,7 +33,6 @@
#include <android-base/chrono_utils.h>
#include <android-base/file.h>
#include <android-base/logging.h>
#include <cutils/android_reboot.h>
#include <private/android_filesystem_config.h>
#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) {

View file

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

View file

@ -39,7 +39,6 @@
#include <android-base/properties.h>
#include <android-base/stringprintf.h>
#include <android-base/strings.h>
#include <cutils/android_reboot.h>
#include <fs_avb/fs_avb.h>
#include <fs_mgr_vendor_overlay.h>
#include <keyutils.h>
@ -605,17 +604,6 @@ void HandleKeychord(const std::vector<int>& 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.

View file

@ -19,8 +19,13 @@
#include <sys/syscall.h>
#include <unistd.h>
#include <android-base/logging.h>
#include <cutils/android_reboot.h>
#include <string>
#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(
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);

View file

@ -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

View file

@ -60,7 +60,6 @@
#include <android-base/logging.h>
#include <android-base/parseint.h>
#include <android-base/unique_fd.h>
#include <cutils/android_reboot.h>
#include <fs_avb/fs_avb.h>
#include <selinux/android.h>
@ -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();

View file

@ -40,6 +40,7 @@
#include <selinux/android.h>
#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<void(const char*)> 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() {

View file

@ -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<void(const char*)> abort_function);
void SetStdioToDevNull(char** argv);
void InitKernelLogging(char** argv);
bool IsRecoveryMode();
} // namespace init
} // namespace android