Unix crash log: Generalise crash log fault handling to all sections
This commit is contained in:
@@ -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
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user