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:
parent
5f7314b7e1
commit
59656fb377
10 changed files with 89 additions and 35 deletions
|
@ -68,6 +68,7 @@ cc_defaults {
|
|||
"libpropertyinfoparser",
|
||||
],
|
||||
shared_libs: [
|
||||
"libbacktrace",
|
||||
"libbase",
|
||||
"libbinder",
|
||||
"libbootloader_message",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in a new issue