From fafbdf92f34d51db63bf0bc11882ff8068c56b7f Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Sat, 25 Feb 2023 15:00:10 -0800 Subject: [PATCH] Make backtrace reporting more crash resistant We now pay the price of six system calls for every word read, when probing the guest memory to generate a listing of call frames. --- blink/blinkenlights.c | 16 ++---------- blink/debug.c | 57 ++++++++++++++++++++++++++++++++++--------- blink/debug.h | 1 + blink/machine.h | 6 ++--- test/func/func.mk | 5 +++- 5 files changed, 55 insertions(+), 30 deletions(-) diff --git a/blink/blinkenlights.c b/blink/blinkenlights.c index 4b701f9f2..83697916e 100644 --- a/blink/blinkenlights.c +++ b/blink/blinkenlights.c @@ -550,18 +550,6 @@ static i64 GetSp(void) { } } -static i64 ReadWord(u8 *p) { - switch (GetPointerWidth()) { - default: - case 8: - return Read64(p); - case 4: - return Read32(p); - case 2: - return Read16(p); - } -} - static void AppendPanel(struct Panel *p, i64 line, const char *s) { if (0 <= line && line < p->bottom - p->top) { AppendStr(&p->lines[line], s); @@ -1858,8 +1846,8 @@ static void DrawFrames(struct Panel *p) { break; } sp = bp; - bp = ReadWord(r + 0); - rp = ReadWord(r + 8); + bp = ReadWordSafely(m->mode, r + 0); + rp = ReadWordSafely(m->mode, r + 8); } } diff --git a/blink/debug.c b/blink/debug.c index 0b5251fc8..615e7b1c7 100644 --- a/blink/debug.c +++ b/blink/debug.c @@ -18,6 +18,8 @@ ╚─────────────────────────────────────────────────────────────────────────────*/ #include #include +#include +#include #include #include #include @@ -26,6 +28,7 @@ #include "blink/assert.h" #include "blink/builtin.h" +#include "blink/bus.h" #include "blink/debug.h" #include "blink/dis.h" #include "blink/endian.h" @@ -40,12 +43,15 @@ #include "blink/thread.h" #include "blink/tsan.h" #include "blink/util.h" +#include "blink/x86.h" #ifdef UNWIND #define UNW_LOCAL_ONLY #include #endif +#define FAKE_WORD 0x6660666066660666 + #define MAX_BACKTRACE_LINES 64 #define APPEND(...) o += snprintf(b + o, n - o, __VA_ARGS__) @@ -54,6 +60,7 @@ int asan_backtrace_index; int asan_backtrace_buffer[1]; int ubsan_backtrace_memory[2]; char *ubsan_backtrace_pointer = ((char *)ubsan_backtrace_memory) + 1; +_Thread_local static jmp_buf g_busted; void PrintBacktrace(void) { #ifdef UNWIND @@ -86,16 +93,42 @@ void PrintBacktrace(void) { #endif } -static i64 ReadWord(struct Machine *m, u8 *p) { - switch (2 << m->mode) { +static void OnBusted(int sig) { + longjmp(g_busted, sig); +} + +static i64 ReadWord(int mode, u8 *p) { + switch (mode) { default: - case 8: - return Read64(p); - case 4: - return Read32(p); - case 2: - return Read16(p); + case XED_MODE_LONG: + return Load64Unlocked(p); + case XED_MODE_LEGACY: + return Load32(p); + case XED_MODE_REAL: + return Load16(p); + } +} + +i64 ReadWordSafely(int mode, u8 *p) { + i64 res; + sigset_t oldss, newss; + struct sigaction oldsa[2]; + struct sigaction newsa = {.sa_handler = OnBusted}; + sigemptyset(&newss); + sigaddset(&newss, SIGBUS); + sigaddset(&newss, SIGSEGV); + sigaction(SIGBUS, &newsa, oldsa + 0); + sigaction(SIGSEGV, &newsa, oldsa + 1); + pthread_sigmask(SIG_UNBLOCK, &newss, &oldss); + if (!setjmp(g_busted)) { + res = ReadWord(mode, p); + } else { + res = FAKE_WORD >> ((8 - (2 << mode)) * 8); } + pthread_sigmask(SIG_SETMASK, &oldss, 0); + sigaction(SIGSEGV, oldsa + 1, 0); + sigaction(SIGBUS, oldsa + 0, 0); + return res; } int GetInstruction(struct Machine *m, i64 pc, struct XedDecodedInst *x) { @@ -218,14 +251,14 @@ const char *GetBacktrace(struct Machine *m) { " FS %016" PRIx64 " " " GS %016" PRIx64 " " "OPS %-16ld " - "JIT %-16ld\n\t" + "FLG %s\n\t" "%s\n\t", m->cs.base + MaskAddress(m->mode, m->ip), DescribeOp(m, GetPc(m)), Get64(m->ax), Get64(m->cx), Get64(m->dx), Get64(m->bx), Get64(m->sp), Get64(m->bp), Get64(m->si), Get64(m->di), Get64(m->r8), Get64(m->r9), Get64(m->r10), Get64(m->r11), Get64(m->r12), Get64(m->r13), Get64(m->r14), Get64(m->r15), m->fs.base, m->gs.base, - GET_COUNTER(instructions_decoded), GET_COUNTER(instructions_jitted), + GET_COUNTER(instructions_decoded), DescribeCpuFlags(m->flags), g_progname); #ifndef DISABLE_BACKTRACE @@ -256,8 +289,8 @@ const char *GetBacktrace(struct Machine *m) { break; } sp = bp; - bp = ReadWord(m, r + 0); - rp = ReadWord(m, r + 8); + bp = ReadWordSafely(m->mode, r + 0); + rp = ReadWordSafely(m->mode, r + 8); } if (m->system->dis == &dis) { DisFree(&dis); diff --git a/blink/debug.h b/blink/debug.h index 9b2c3f370..d937351bd 100644 --- a/blink/debug.h +++ b/blink/debug.h @@ -8,6 +8,7 @@ void PrintBacktrace(void); void DumpHex(u8 *, size_t); void PrintFds(struct Fds *); +i64 ReadWordSafely(int, u8 *); const char *DescribeProt(int); const char *DescribeMopcode(int); const char *DescribeCpuFlags(int); diff --git a/blink/machine.h b/blink/machine.h index b53ee61ec..767b388b5 100644 --- a/blink/machine.h +++ b/blink/machine.h @@ -725,12 +725,12 @@ MICRO_OP_SAFE u8 Cpl(struct Machine *m) { #define BEGIN_NO_PAGE_FAULTS \ { \ - bool nofault; \ - nofault = m->nofault; \ + bool nofault_; \ + nofault_ = m->nofault; \ m->nofault = true #define END_NO_PAGE_FAULTS \ - m->nofault = nofault; \ + m->nofault = nofault_; \ } #endif /* BLINK_MACHINE_H_ */ diff --git a/test/func/func.mk b/test/func/func.mk index 2873ee11f..df42b9160 100644 --- a/test/func/func.mk +++ b/test/func/func.mk @@ -8,7 +8,10 @@ TEST_FUNC_OBJS = $(TEST_FUNC_SRCS:%.c=o/$(MODE)/x86_64/%.o) TEST_FUNC_BINS = $(TEST_FUNC_SRCS:%.c=o/$(MODE)/%.elf) TEST_FUNC_COMS = $(TEST_FUNC_SRCS:%.c=o/$(MODE)/%.com) TEST_FUNC_CHECKS = $(TEST_FUNC_SRCS:%.c=o/$(MODE)/%.com.ok) -TEST_FUNC_EMULATES = $(foreach ARCH,$(ARCHITECTURES),$(foreach SRC,$(TEST_FUNC_SRCS),$(SRC:%.c=o/$(MODE)/$(ARCH)/%.elf.emulates))) +TEST_FUNC_EMULATES = $(foreach ARCH,$(ARCHITECTURES),$(foreach SRC,$(filter-out $(TEST_FUNC_NOEMU),$(TEST_FUNC_SRCS)),$(SRC:%.c=o/$(MODE)/$(ARCH)/%.elf.emulates))) + +# qemu static doesn't appear to emulate this correctly +TEST_FUNC_NOEMU = test/func/busted_test.c TEST_FUNC_LINK = \ $(VM) \