Unix crash log: Generalise crash log fault handling to all sections

This commit is contained in:
Jonathan G Rennison
2023-06-12 18:34:37 +01:00
parent fdcfa7318b
commit dd57fc6ecf
3 changed files with 213 additions and 103 deletions

View File

@@ -60,18 +60,25 @@
#include "../../safeguards.h"
/** The signals we want our crash handler to handle. */
static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGQUIT };
#if defined(__GLIBC__) && defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3))
#pragma GCC diagnostic ignored "-Wclobbered"
#endif
#if defined(__GLIBC__)
static char *logStacktraceSavedBuffer;
static jmp_buf logStacktraceJmpBuf;
#if defined(__GLIBC__) && defined(WITH_SIGACTION)
static char *internal_fault_saved_buffer;
static jmp_buf internal_fault_jmp_buf;
sigset_t internal_fault_old_sig_proc_mask;
static void LogStacktraceSigSegvHandler(int sig)
static void InternalFaultSigHandler(int sig)
{
signal(SIGSEGV, SIG_DFL);
longjmp(logStacktraceJmpBuf, 1);
longjmp(internal_fault_jmp_buf, sig);
if (internal_fault_saved_buffer == nullptr) {
/* if we get here, things are unrecoverable */
_exit(43);
}
}
#endif
@@ -318,6 +325,7 @@ class CrashLogUnix : public CrashLog {
}
}
#endif
this->CrashLogFaultSectionCheckpoint(buffer);
buffer += seprintf(buffer, last,
" Message: %s\n\n",
message == nullptr ? "<none>" : message
@@ -490,8 +498,6 @@ class CrashLogUnix : public CrashLog {
* backtrace() is prone to crashing if the stack is invalid.
* Also these functions freely use malloc which is not technically OK in a signal handler, as
* malloc is not re-entrant.
* For that reason, set up another SIGSEGV handler to handle the case where we trigger a SIGSEGV
* during the course of getting the backtrace.
*
* If libdl is present, try to use that to get the section file name and possibly the symbol
* name/address instead of using the string from backtrace_symbols().
@@ -500,32 +506,12 @@ class CrashLogUnix : public CrashLog {
* and knows about more symbols than libdl does.
* If demangling support is available, try to demangle whatever symbol name we got back.
* If we could find a symbol address from libdl or libbfd, show the offset from that to the frame address.
*
* Note that GCC complains about 'buffer' being clobbered by the longjmp.
* This is not an issue as we save/restore it explicitly, so silence the warning.
*/
char *LogStacktrace(char *buffer, const char *last) const override
{
buffer += seprintf(buffer, last, "Stacktrace:\n");
#if defined(__GLIBC__)
logStacktraceSavedBuffer = buffer;
if (setjmp(logStacktraceJmpBuf) != 0) {
buffer = logStacktraceSavedBuffer;
buffer += seprintf(buffer, last, "\nSomething went seriously wrong when attempting to decode the stacktrace (SIGSEGV in signal handler)\n");
buffer += seprintf(buffer, last, "This is probably due to either: a crash caused by an attempt to call an invalid function\n");
buffer += seprintf(buffer, last, "pointer, some form of stack corruption, or an attempt was made to call malloc recursively.\n\n");
return buffer;
}
signal(SIGSEGV, LogStacktraceSigSegvHandler);
sigset_t sigs;
sigset_t oldsigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGSEGV);
sigprocmask(SIG_UNBLOCK, &sigs, &oldsigs);
void *trace[64];
int trace_size = backtrace(trace, lengthof(trace));
@@ -637,9 +623,6 @@ class CrashLogUnix : public CrashLog {
}
free(messages);
signal(SIGSEGV, SIG_DFL);
sigprocmask(SIG_SETMASK, &oldsigs, nullptr);
/* end of __GLIBC__ */
#elif defined(SUNOS)
ucontext_t uc;
@@ -658,36 +641,92 @@ class CrashLogUnix : public CrashLog {
return buffer + seprintf(buffer, last, "\n");
}
#if defined(USE_SCOPE_INFO) && defined(__GLIBC__)
/**
* This is a wrapper around the generic LogScopeInfo function which sets
* up a signal handler to catch any SIGSEGVs which may occur due to invalid data
*/
/* virtual */ char *LogScopeInfo(char *buffer, const char *last) const override
#if defined(__GLIBC__) && defined(WITH_SIGACTION)
/* virtual */ void StartCrashLogFaultHandler() override
{
logStacktraceSavedBuffer = buffer;
internal_fault_saved_buffer = nullptr;
if (setjmp(logStacktraceJmpBuf) != 0) {
buffer = logStacktraceSavedBuffer;
buffer += seprintf(buffer, last, "\nSomething went seriously wrong when attempting to dump the scope info (SIGSEGV in signal handler).\n");
sigset_t sigs;
sigemptyset(&sigs);
for (int signum : _signals_to_handle) {
sigaddset(&sigs, signum);
}
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_RESTART;
sa.sa_handler = InternalFaultSigHandler;
sa.sa_mask = sigs;
for (int signum : _signals_to_handle) {
sigaction(signum, &sa, nullptr);
}
sigprocmask(SIG_UNBLOCK, &sigs, &internal_fault_old_sig_proc_mask);
}
/* virtual */ void StopCrashLogFaultHandler() override
{
internal_fault_saved_buffer = nullptr;
for (int signum : _signals_to_handle) {
signal(signum, SIG_DFL);
}
sigprocmask(SIG_SETMASK, &internal_fault_old_sig_proc_mask, nullptr);
}
/**
* Set up further signal handlers to handle the case where we trigger another fault signal
* during the course of calling the given log section writer.
*
* If a signal does occur, restore the buffer pointer to either the original value, or
* the value provided in any later checkpoint.
* Insert a message describing the problem and give up on the section.
*
* Note that GCC complains about 'buffer' being clobbered by the longjmp.
* This is not an issue as we save/restore it explicitly, so silence the warning.
*/
/* virtual */ char *TryCrashLogFaultSection(char *buffer, const char *last, const char *section_name, CrashLogSectionWriter writer) override
{
this->FlushCrashLogBuffer();
internal_fault_saved_buffer = buffer;
int signum = setjmp(internal_fault_jmp_buf);
if (signum != 0) {
if (internal_fault_saved_buffer == nullptr) {
/* if we get here, things are unrecoverable */
_exit(43);
}
buffer = internal_fault_saved_buffer;
internal_fault_saved_buffer = nullptr;
buffer += seprintf(buffer, last, "\nSomething went seriously wrong when attempting to fill the '%s' section of the crash log: signal: %s (%d).\n", section_name, strsignal(signum), signum);
buffer += seprintf(buffer, last, "This is probably due to an invalid pointer or other corrupt data.\n\n");
sigset_t sigs;
sigemptyset(&sigs);
for (int signum : _signals_to_handle) {
sigaddset(&sigs, signum);
}
sigprocmask(SIG_UNBLOCK, &sigs, nullptr);
return buffer;
}
signal(SIGSEGV, LogStacktraceSigSegvHandler);
sigset_t sigs;
sigset_t oldsigs;
sigemptyset(&sigs);
sigaddset(&sigs, SIGSEGV);
sigprocmask(SIG_UNBLOCK, &sigs, &oldsigs);
buffer = this->CrashLog::LogScopeInfo(buffer, last);
signal(SIGSEGV, SIG_DFL);
sigprocmask(SIG_SETMASK, &oldsigs, nullptr);
buffer = writer(this, buffer, last);
internal_fault_saved_buffer = nullptr;
return buffer;
}
#endif
/* virtual */ void CrashLogFaultSectionCheckpoint(char *buffer) const override
{
if (internal_fault_saved_buffer != nullptr && buffer > internal_fault_saved_buffer) {
internal_fault_saved_buffer = buffer;
}
const_cast<CrashLogUnix *>(this)->FlushCrashLogBuffer();
}
#endif /* __GLIBC__ && WITH_SIGACTION */
public:
struct DesyncTag {};
@@ -730,9 +769,6 @@ public:
}
};
/** The signals we want our crash handler to handle. */
static const int _signals_to_handle[] = { SIGSEGV, SIGABRT, SIGFPE, SIGBUS, SIGILL, SIGQUIT };
/**
* Entry point for the crash handler.
* @note Not static so it shows up in the backtrace.
@@ -745,8 +781,8 @@ static void CDECL HandleCrash(int signum)
#endif
{
/* Disable all handling of signals by us, so we don't go into infinite loops. */
for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
signal(*i, SIG_DFL);
for (int signum : _signals_to_handle) {
signal(signum, SIG_DFL);
}
const char *abort_reason = CrashLog::GetAbortCrashlogReason();
@@ -782,7 +818,7 @@ static void CDECL HandleCrash(int signum)
ss.ss_flags = 0;
sigaltstack(&ss, nullptr);
#endif
for (const int *i = _signals_to_handle; i != endof(_signals_to_handle); i++) {
for (int signum : _signals_to_handle) {
#ifdef WITH_SIGACTION
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
@@ -792,9 +828,9 @@ static void CDECL HandleCrash(int signum)
#endif
sigemptyset(&sa.sa_mask);
sa.sa_sigaction = HandleCrash;
sigaction(*i, &sa, nullptr);
sigaction(signum, &sa, nullptr);
#else
signal(*i, HandleCrash);
signal(signum, HandleCrash);
#endif
}
}