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) \