From 55944e5e40b1be2afc4855d8d2baf4b73d1876b5 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 10 Apr 2024 22:49:52 +0200 Subject: Adding upstream version 255.4. Signed-off-by: Daniel Baumann --- src/core/crash-handler.c | 193 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 193 insertions(+) create mode 100644 src/core/crash-handler.c (limited to 'src/core/crash-handler.c') diff --git a/src/core/crash-handler.c b/src/core/crash-handler.c new file mode 100644 index 0000000..f5c31b6 --- /dev/null +++ b/src/core/crash-handler.c @@ -0,0 +1,193 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include + +#include "sd-messages.h" + +#include "crash-handler.h" +#include "exit-status.h" +#include "macro.h" +#include "main.h" +#include "missing_syscall.h" +#include "process-util.h" +#include "raw-clone.h" +#include "rlimit-util.h" +#include "signal-util.h" +#include "terminal-util.h" +#include "virt.h" + +_noreturn_ void freeze_or_exit_or_reboot(void) { + + /* If we are running in a container, let's prefer exiting, after all we can propagate an exit code to + * the container manager, and thus inform it that something went wrong. */ + if (detect_container() > 0) { + log_struct(LOG_EMERG, + LOG_MESSAGE("Exiting PID 1..."), + "MESSAGE_ID=" SD_MESSAGE_CRASH_EXIT_STR); + _exit(EXIT_EXCEPTION); + } + + if (arg_crash_reboot) { + log_notice("Rebooting in 10s..."); + (void) sleep(10); + + log_notice("Rebooting now..."); + (void) reboot(RB_AUTOBOOT); + log_struct_errno(LOG_EMERG, errno, + LOG_MESSAGE("Failed to reboot: %m"), + "MESSAGE_ID=" SD_MESSAGE_CRASH_FAILED_STR); + } + + log_struct(LOG_EMERG, + LOG_MESSAGE("Freezing execution."), + "MESSAGE_ID=" SD_MESSAGE_CRASH_FREEZE_STR); + sync(); + freeze(); +} + +_noreturn_ static void crash(int sig, siginfo_t *siginfo, void *context) { + struct sigaction sa; + pid_t pid; + + /* NB: 💣 💣 💣 This is a signal handler, most likely executed in a situation where we have corrupted + * memory. Thus: please avoid any libc memory allocation here, or any functions that internally use + * memory allocation, as we cannot rely on memory allocation still working at this point! (Note that + * memory allocation is not async-signal-safe anyway — see signal-safety(7) for details —, and thus + * is not permissible in signal handlers.) */ + + if (getpid_cached() != 1) + /* Pass this on immediately, if this is not PID 1 */ + propagate_signal(sig, siginfo); + else if (!arg_dump_core) + log_struct(LOG_EMERG, + LOG_MESSAGE("Caught <%s>, not dumping core.", signal_to_string(sig)), + "MESSAGE_ID=" SD_MESSAGE_CRASH_NO_COREDUMP_STR); + else { + sa = (struct sigaction) { + .sa_handler = nop_signal_handler, + .sa_flags = SA_NOCLDSTOP|SA_RESTART, + }; + + /* We want to wait for the core process, hence let's enable SIGCHLD */ + (void) sigaction(SIGCHLD, &sa, NULL); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_struct_errno(LOG_EMERG, errno, + LOG_MESSAGE("Caught <%s>, cannot fork for core dump: %m", signal_to_string(sig)), + "MESSAGE_ID=" SD_MESSAGE_CRASH_NO_FORK_STR); + else if (pid == 0) { + /* Enable default signal handler for core dump */ + + sa = (struct sigaction) { + .sa_handler = SIG_DFL, + }; + (void) sigaction(sig, &sa, NULL); + + /* Don't limit the coredump size */ + (void) setrlimit(RLIMIT_CORE, &RLIMIT_MAKE_CONST(RLIM_INFINITY)); + + /* Just to be sure... */ + (void) chdir("/"); + + /* Raise the signal again */ + propagate_signal(sig, siginfo); + assert_not_reached(); + _exit(EXIT_EXCEPTION); + } else { + siginfo_t status; + int r; + + if (siginfo) { + if (siginfo->si_pid == 0) + log_struct(LOG_EMERG, + LOG_MESSAGE("Caught <%s>, from unknown sender process.", signal_to_string(sig)), + "MESSAGE_ID=" SD_MESSAGE_CRASH_UNKNOWN_SIGNAL_STR); + else if (siginfo->si_pid == 1) + log_struct(LOG_EMERG, + LOG_MESSAGE("Caught <%s>, from our own process.", signal_to_string(sig)), + "MESSAGE_ID=" SD_MESSAGE_CRASH_SYSTEMD_SIGNAL_STR); + else + log_struct(LOG_EMERG, + LOG_MESSAGE("Caught <%s> from PID "PID_FMT".", signal_to_string(sig), siginfo->si_pid), + "MESSAGE_ID=" SD_MESSAGE_CRASH_PROCESS_SIGNAL_STR); + } + + /* Order things nicely. */ + r = wait_for_terminate(pid, &status); + if (r < 0) + log_struct_errno(LOG_EMERG, r, + LOG_MESSAGE("Caught <%s>, waitpid() failed: %m", signal_to_string(sig)), + "MESSAGE_ID=" SD_MESSAGE_CRASH_WAITPID_FAILED_STR); + else if (status.si_code != CLD_DUMPED) { + const char *s = status.si_code == CLD_EXITED ? + exit_status_to_string(status.si_status, EXIT_STATUS_LIBC) : + signal_to_string(status.si_status); + + log_struct(LOG_EMERG, + LOG_MESSAGE("Caught <%s>, core dump failed (child "PID_FMT", code=%s, status=%i/%s).", + signal_to_string(sig), + pid, + sigchld_code_to_string(status.si_code), + status.si_status, + strna(s)), + "MESSAGE_ID=" SD_MESSAGE_CRASH_COREDUMP_FAILED_STR); + } else + log_struct(LOG_EMERG, + LOG_MESSAGE("Caught <%s>, dumped core as pid "PID_FMT".", + signal_to_string(sig), pid), + "MESSAGE_ID=" SD_MESSAGE_CRASH_COREDUMP_PID_STR); + } + } + + if (arg_crash_chvt >= 0) + (void) chvt(arg_crash_chvt); + + sa = (struct sigaction) { + .sa_handler = SIG_IGN, + .sa_flags = SA_NOCLDSTOP|SA_NOCLDWAIT|SA_RESTART, + }; + + /* Let the kernel reap children for us */ + (void) sigaction(SIGCHLD, &sa, NULL); + + if (arg_crash_shell) { + log_notice("Executing crash shell in 10s..."); + (void) sleep(10); + + pid = raw_clone(SIGCHLD); + if (pid < 0) + log_struct_errno(LOG_EMERG, errno, + LOG_MESSAGE("Failed to fork off crash shell: %m"), + "MESSAGE_ID=" SD_MESSAGE_CRASH_SHELL_FORK_FAILED_STR); + else if (pid == 0) { + (void) setsid(); + (void) make_console_stdio(); + (void) rlimit_nofile_safe(); + (void) execle("/bin/sh", "/bin/sh", NULL, environ); + + log_struct_errno(LOG_EMERG, errno, + LOG_MESSAGE("execle() failed: %m"), + "MESSAGE_ID=" SD_MESSAGE_CRASH_EXECLE_FAILED_STR); + _exit(EXIT_EXCEPTION); + } else { + log_info("Spawned crash shell as PID "PID_FMT".", pid); + (void) wait_for_terminate(pid, NULL); + } + } + + freeze_or_exit_or_reboot(); +} + +void install_crash_handler(void) { + static const struct sigaction sa = { + .sa_sigaction = crash, + .sa_flags = SA_NODEFER | SA_SIGINFO, /* So that we can raise the signal again from the signal handler */ + }; + int r; + + /* We ignore the return value here, since, we don't mind if we cannot set up a crash handler */ + r = sigaction_many(&sa, SIGNALS_CRASH_HANDLER); + if (r < 0) + log_debug_errno(r, "I had trouble setting up the crash handler, ignoring: %m"); +} -- cgit v1.2.3