From 073cea612836c50e473640f729913bccaaf53c9a Mon Sep 17 00:00:00 2001 From: Kuba Mracek Date: Sat, 26 Nov 2016 00:50:08 +0000 Subject: [PATCH] [asan] Add a "dump_registers" flag to print out CPU registers after a SIGSEGV This patch prints out all CPU registers after a SIGSEGV. These are available in the signal handler context. Only implemented for Darwin. Can be turned off with the dump_registers flag. Differential Revision: https://reviews.llvm.org/D11365 llvm-svn: 287957 --- compiler-rt/lib/asan/asan_errors.cc | 6 +++ compiler-rt/lib/asan/asan_flags.inc | 3 ++ .../lib/sanitizer_common/sanitizer_common.h | 2 + .../lib/sanitizer_common/sanitizer_linux.cc | 4 ++ compiler-rt/lib/sanitizer_common/sanitizer_mac.cc | 50 ++++++++++++++++++++++ compiler-rt/lib/sanitizer_common/sanitizer_win.cc | 4 ++ .../test/asan/TestCases/Darwin/dump_registers.cc | 26 +++++++++++ 7 files changed, 95 insertions(+) create mode 100644 compiler-rt/test/asan/TestCases/Darwin/dump_registers.cc diff --git a/compiler-rt/lib/asan/asan_errors.cc b/compiler-rt/lib/asan/asan_errors.cc index a571a71..7f754d5 100644 --- a/compiler-rt/lib/asan/asan_errors.cc +++ b/compiler-rt/lib/asan/asan_errors.cc @@ -53,6 +53,11 @@ static void MaybeDumpInstructionBytes(uptr pc) { Report("%s", str.data()); } +static void MaybeDumpRegisters(void *context) { + if (!flags()->dump_registers) return; + SignalContext::DumpAllRegisters(context); +} + void ErrorDeadlySignal::Print() { Decorator d; Printf("%s", d.Warning()); @@ -78,6 +83,7 @@ void ErrorDeadlySignal::Print() { common_flags()->fast_unwind_on_fatal); stack.Print(); MaybeDumpInstructionBytes(pc); + MaybeDumpRegisters(context); Printf("AddressSanitizer can not provide additional info.\n"); ReportErrorSummary(description, &stack); } diff --git a/compiler-rt/lib/asan/asan_flags.inc b/compiler-rt/lib/asan/asan_flags.inc index ea63383..5272477 100644 --- a/compiler-rt/lib/asan/asan_flags.inc +++ b/compiler-rt/lib/asan/asan_flags.inc @@ -133,6 +133,9 @@ ASAN_FLAG(int, detect_odr_violation, 2, "have different sizes") ASAN_FLAG(bool, dump_instruction_bytes, false, "If true, dump 16 bytes starting at the instruction that caused SEGV") +ASAN_FLAG(bool, dump_registers, true, + "If true, dump values of CPU registers when SEGV happens. Only " + "available on OS X for now.") ASAN_FLAG(const char *, suppressions, "", "Suppressions file name.") ASAN_FLAG(bool, halt_on_error, true, "Crash the program after printing the first error report " diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_common.h b/compiler-rt/lib/sanitizer_common/sanitizer_common.h index 499bc07..5073e0d 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_common.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_common.h @@ -798,6 +798,8 @@ struct SignalContext { is_memory_access(is_memory_access), write_flag(write_flag) {} + static void DumpAllRegisters(void *context); + // Creates signal context in a platform-specific manner. static SignalContext Create(void *siginfo, void *context); diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc index f08cf1d..76cdc72 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_linux.cc @@ -1304,6 +1304,10 @@ SignalContext::WriteFlag SignalContext::GetWriteFlag(void *context) { #endif } +void SignalContext::DumpAllRegisters(void *context) { + // FIXME: Implement this. +} + void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { #if defined(__arm__) ucontext_t *ucontext = (ucontext_t*)context; diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc index 4a299e9..3c1f216 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_mac.cc @@ -793,6 +793,56 @@ uptr FindAvailableMemoryRange(uptr shadow_size, // FIXME implement on this platform. void GetMemoryProfile(fill_profile_f cb, uptr *stats, uptr stats_size) { } +void SignalContext::DumpAllRegisters(void *context) { + Report("Register values:\n"); + + ucontext_t *ucontext = (ucontext_t*)context; +# define DUMPREG64(r) \ + Printf("%s = 0x%016llx ", #r, ucontext->uc_mcontext->__ss.__ ## r); +# define DUMPREG32(r) \ + Printf("%s = 0x%08x ", #r, ucontext->uc_mcontext->__ss.__ ## r); +# define DUMPREG_(r) Printf(" "); DUMPREG(r); +# define DUMPREG__(r) Printf(" "); DUMPREG(r); +# define DUMPREG___(r) Printf(" "); DUMPREG(r); + +# if defined(__x86_64__) +# define DUMPREG(r) DUMPREG64(r) + DUMPREG(rax); DUMPREG(rbx); DUMPREG(rcx); DUMPREG(rdx); Printf("\n"); + DUMPREG(rdi); DUMPREG(rsi); DUMPREG(rbp); DUMPREG(rsp); Printf("\n"); + DUMPREG_(r8); DUMPREG_(r9); DUMPREG(r10); DUMPREG(r11); Printf("\n"); + DUMPREG(r12); DUMPREG(r13); DUMPREG(r14); DUMPREG(r15); Printf("\n"); +# elif defined(__i386__) +# define DUMPREG(r) DUMPREG32(r) + DUMPREG(eax); DUMPREG(ebx); DUMPREG(ecx); DUMPREG(edx); Printf("\n"); + DUMPREG(edi); DUMPREG(esi); DUMPREG(ebp); DUMPREG(esp); Printf("\n"); +# elif defined(__aarch64__) +# define DUMPREG(r) DUMPREG64(r) + DUMPREG_(x[0]); DUMPREG_(x[1]); DUMPREG_(x[2]); DUMPREG_(x[3]); Printf("\n"); + DUMPREG_(x[4]); DUMPREG_(x[5]); DUMPREG_(x[6]); DUMPREG_(x[7]); Printf("\n"); + DUMPREG_(x[8]); DUMPREG_(x[9]); DUMPREG(x[10]); DUMPREG(x[11]); Printf("\n"); + DUMPREG(x[12]); DUMPREG(x[13]); DUMPREG(x[14]); DUMPREG(x[15]); Printf("\n"); + DUMPREG(x[16]); DUMPREG(x[17]); DUMPREG(x[18]); DUMPREG(x[19]); Printf("\n"); + DUMPREG(x[20]); DUMPREG(x[21]); DUMPREG(x[22]); DUMPREG(x[23]); Printf("\n"); + DUMPREG(x[24]); DUMPREG(x[25]); DUMPREG(x[26]); DUMPREG(x[27]); Printf("\n"); + DUMPREG(x[28]); DUMPREG___(fp); DUMPREG___(lr); DUMPREG___(sp); Printf("\n"); +# elif defined(__arm__) +# define DUMPREG(r) DUMPREG32(r) + DUMPREG_(r[0]); DUMPREG_(r[1]); DUMPREG_(r[2]); DUMPREG_(r[3]); Printf("\n"); + DUMPREG_(r[4]); DUMPREG_(r[5]); DUMPREG_(r[6]); DUMPREG_(r[7]); Printf("\n"); + DUMPREG_(r[8]); DUMPREG_(r[9]); DUMPREG(r[10]); DUMPREG(r[11]); Printf("\n"); + DUMPREG(r[12]); DUMPREG___(sp); DUMPREG___(lr); DUMPREG___(pc); Printf("\n"); +# else +# error "Unknown architecture" +# endif + +# undef DUMPREG64 +# undef DUMPREG32 +# undef DUMPREG_ +# undef DUMPREG__ +# undef DUMPREG___ +# undef DUMPREG +} + } // namespace __sanitizer #endif // SANITIZER_MAC diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_win.cc b/compiler-rt/lib/sanitizer_common/sanitizer_win.cc index d0c3d36..763193a7 100644 --- a/compiler-rt/lib/sanitizer_common/sanitizer_win.cc +++ b/compiler-rt/lib/sanitizer_common/sanitizer_win.cc @@ -872,6 +872,10 @@ SignalContext SignalContext::Create(void *siginfo, void *context) { write_flag); } +void SignalContext::DumpAllRegisters(void *context) { + // FIXME: Implement this. +} + uptr ReadBinaryName(/*out*/char *buf, uptr buf_len) { // FIXME: Actually implement this function. CHECK_GT(buf_len, 0); diff --git a/compiler-rt/test/asan/TestCases/Darwin/dump_registers.cc b/compiler-rt/test/asan/TestCases/Darwin/dump_registers.cc new file mode 100644 index 0000000..884ad2e --- /dev/null +++ b/compiler-rt/test/asan/TestCases/Darwin/dump_registers.cc @@ -0,0 +1,26 @@ +// Check that ASan dumps the CPU registers on a SIGSEGV. + +// RUN: %clangxx_asan %s -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +#include +#include + +int main() { + fprintf(stderr, "Hello\n"); + char *ptr; + + if (sizeof(void *) == 8) + ptr = (char *)0x6666666666666666; + else if (sizeof(void *) == 4) + ptr = (char *)0x55555555; + else + assert(0 && "Your computer is weird."); + + char c = *ptr; // BOOM + // CHECK: ERROR: AddressSanitizer: SEGV + // CHECK: Register values: + // CHECK: {{0x55555555|0x6666666666666666}} + fprintf(stderr, "World\n"); + return c; +} -- 2.7.4