Skip to content

Commit

Permalink
Make backtrace reporting more crash resistant
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
jart committed Feb 26, 2023
1 parent 99c7a3a commit fafbdf9
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 30 deletions.
16 changes: 2 additions & 14 deletions blink/blinkenlights.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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);
}
}

Expand Down
57 changes: 45 additions & 12 deletions blink/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
╚─────────────────────────────────────────────────────────────────────────────*/
#include <errno.h>
#include <fcntl.h>
#include <setjmp.h>
#include <signal.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
Expand All @@ -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"
Expand All @@ -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 <libunwind.h>
#endif

#define FAKE_WORD 0x6660666066660666

#define MAX_BACKTRACE_LINES 64

#define APPEND(...) o += snprintf(b + o, n - o, __VA_ARGS__)
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions blink/debug.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
6 changes: 3 additions & 3 deletions blink/machine.h
Original file line number Diff line number Diff line change
Expand Up @@ -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_ */
5 changes: 4 additions & 1 deletion test/func/func.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down

0 comments on commit fafbdf9

Please sign in to comment.