Index: lib/sanitizer_common/sanitizer_stacktrace.h =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace.h +++ lib/sanitizer_common/sanitizer_stacktrace.h @@ -43,15 +43,20 @@ u32 size; u32 tag; + // Nonzero if the stack trace was generated in response to a signal. + bool signaled; + static const int TAG_UNKNOWN = 0; static const int TAG_ALLOC = 1; static const int TAG_DEALLOC = 2; static const int TAG_CUSTOM = 100; // Tool specific tags start here. - StackTrace() : trace(nullptr), size(0), tag(0) {} - StackTrace(const uptr *trace, u32 size) : trace(trace), size(size), tag(0) {} + StackTrace() : trace(nullptr), size(0), signaled(false) {} + StackTrace(const uptr *trace, uptr size) + : trace(trace), size(size), signaled(false) {} + StackTrace(const uptr *trace, u32 size, u32 tag) - : trace(trace), size(size), tag(tag) {} + : trace(trace), size(size), tag(tag), signaled(false) {} // Prints a symbolized stacktrace, followed by an empty line. void Print() const; Index: lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ lib/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -27,12 +27,22 @@ InternalScopedString frame_desc(GetPageSizeCached() * 2); uptr frame_num = 0; for (uptr i = 0; i < size && trace[i]; i++) { - // PCs in stack traces are actually the return addresses, that is, - // addresses of the next instructions after the call. - uptr pc = GetPreviousInstructionPc(trace[i]); + // Except when the stack trace is being generated in response to + // a signal such as SIGSEGV caused by executing an instruction + // and the frame is #0, decrementing the PC recorded on the stack + // helps find the expected corresponding line number in the debug + // info. This is because with the exception of frame #0 the PC saved + // on the stack is the return address, that is, the address of the + // next instruction after a branch and link or call instruction + // (or after the delay slot after it, if one is present). + const int adjust = (0 == i) && signaled ? 0 + : trace[i] - GetPreviousInstructionPc(trace[i]); + const uptr pc = trace[i] - adjust; SymbolizedStack *frames = Symbolizer::GetOrInit()->SymbolizePC(pc); CHECK(frames); for (SymbolizedStack *cur = frames; cur; cur = cur->next) { + // Restore the PC to match the stack trace. + cur->info.address += adjust; frame_desc.clear(); RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++, cur->info, common_flags()->symbolize_vs_style, @@ -48,6 +58,8 @@ void BufferedStackTrace::Unwind(u32 max_depth, uptr pc, uptr bp, void *context, uptr stack_top, uptr stack_bottom, bool request_fast_unwind) { + // Record that the stack trace was generated as a result of a signal. + signaled = 0 != context; top_frame_bp = (max_depth > 0) ? bp : 0; // Avoid doing any work for small max_depth. if (max_depth == 0) { Index: test/asan/TestCases/print-stack-trace-pc-on-signal.cc =================================================================== --- test/asan/TestCases/print-stack-trace-pc-on-signal.cc +++ test/asan/TestCases/print-stack-trace-pc-on-signal.cc @@ -0,0 +1,206 @@ +// RUN: %clangxx_asan -O0 %s -o %t && ASAN_OPTIONS='stack_trace_format="#%%n %%p %%f:%%l"' not %run %t 2>&1 | FileCheck --check-prefix=CHECK-$(pfx=$(uname); case $pfx in Linux) echo $pfx;; *) echo other;; esac) %s +// RUN: %clangxx_asan -O3 %s -o %t && ASAN_OPTIONS='stack_trace_format="#%%n %%p %%f:%%l"' not %run %t 2>&1 | FileCheck --check-prefix=CHECK-$(pfx=$(uname); case $pfx in Linux) echo $pfx;; *) echo other;; esac) %s + +#include + +#include +#include + +// Allow USE_FORK_AND_SIGACTION to be defined to 0 to test the alternate +// (non-GLIBC) code path. +#ifndef USE_FORK_AND_SIGACTION +# if 1 < __GLIBC__ + // MCONTEXT_GET_PC(mcontext_t*) returns the instruction pointer + // value from the machine specific mcontext_t member of struct + // ucontext_t. +# if __arm__ +# define MCONTEXT_GET_PC(mctx) (mctx)->arm_pc +# elif __powerpc64__ +# define MCONTEXT_GET_PC(mctx) (mctx)->regs->nip +# elif __x86_64__ +# define MCONTEXT_GET_PC(mctx) (mctx)->gregs[REG_RIP] +# elif __i386__ +# define MCONTEXT_GET_PC(mctx) (mctx)->gregs[REG_EIP] +# endif +# endif +# ifdef MCONTEXT_GET_PC +# define USE_FORK_AND_SIGACTION 1 +# include +# include +# include +# include +# endif +#endif + +#ifndef USE_FORK_AND_SIGACTION +# define USE_FORK_AND_SIGACTION 0 +#endif + +static struct StackFrame { + const void *pc; + const char *func; + int line; +} stack[4], *pfrm = stack; + +// Set to true if the test should verify the address of the faulting +// instruction in a stack trace triggered by a SEGV as opposed to one +// printed by __sanitizer_print_stack_trace. +static bool trigger_segv; + +// Print to stderr the process stack trace obtained by calling +// __builtin_return_address in each test function. Calling stdio +// functions in an asynchronous signal handler is normally undefined +// but it's safe in this case. +static void print_stack_trace(size_t faultpc) { + const StackFrame *pf = pfrm; + fprintf(stderr, "Stack trace:\n"); + for (int i = 0; pf-- != stack; ++i) { + const size_t pc = i ? reinterpret_cast(pf[1].pc) : faultpc; + fprintf(stderr, ">%i %#zx %s:%i\n", i, pc, pf->func, pf->line); + } + fputc('\n', stderr); +} + +#if USE_FORK_AND_SIGACTION + +extern "C" { +// Handle the SEGV signal by storing the address of the faulting +// instruction, printing the stack trace, and successfully exiting +// the process. +static void handle_segv(int signo, siginfo_t*, void *arg) { + const ucontext_t* const ctx = (ucontext_t *)arg; + const size_t faultpc = MCONTEXT_GET_PC(&ctx->uc_mcontext); + print_stack_trace(faultpc); + _Exit(0); +} +} // extern "C" + +// Set up a handler for the SEGV signal, bypassing the santizer's +// interception machinery that would otherwise prevents the calling +// process from doing so. +static bool maybe_setup_segv_handler(bool reset) { + struct sigaction act = { }; + struct sigaction prev_act = { }; + act.sa_handler = 0; + // Reset the signal disposition to default after delivery. + // Invoke the three-argument handler when a signal is delivered. + act.sa_flags = SA_RESETHAND | SA_SIGINFO; + act.sa_sigaction = handle_segv; + // Get the address of libc's sigaction (not the one interposed + // by the sanitizer). + if (void* const libc_sigaction = dlsym(RTLD_NEXT, "sigaction")) { + typedef int sigaction_func(int, + const struct sigaction*, + struct sigaction*); + sigaction_func* const psa = (sigaction_func*)libc_sigaction; + if (psa(SIGSEGV, &act, &prev_act)) + return false; + // If successful and reset is true, reset the handler to its + // previous state and return an indication of success. + if (reset) + psa(SIGSEGV, &prev_act, &act); + + return true; + } + return false; +} + +#else // do not use fork and sigaction + +// Don't know how to set up a SEGV handler outside of POSIX. +static bool maybe_setup_segv_handler() { + return false; +} + +#endif // USE_FORK_AND_SIGACTION + + +// Line numbers to include in both stack traces and to compare +// for equality in CHECK directives (see below). +#define FOO_LINE 101 +#define BAR_LINE 201 +#define MAIN_LINE 301 + +#define ADD_FRAME(line) \ + *pfrm++ = (StackFrame) { __builtin_return_address(0), __func__, line } + +void __attribute__((weak)) foo(int *p) { + ADD_FRAME(FOO_LINE); + if (trigger_segv) { +#line FOO_LINE + *p = *p + 1; + // The following line should never be reached since the process + // exits as a result of the SIGSEGV generated in response to + // the null pointer derefernce above. + } else { + // Print a stack trace without the address of the "faulting" + // instruction (that would have been executed in the block + // above if the macro USE_FORK_AND_SIGACTION had been defined. + print_stack_trace(0); +#line FOO_LINE + __sanitizer_print_stack_trace(); + } + _Exit(0); +} + +void __attribute__((weak)) bar(int *p) { + ADD_FRAME(BAR_LINE); +#line BAR_LINE + foo(p); + // Call foo again to prevent tail call optimization from eliminating + // bar's stack frame from the trace. + foo(p); +} + +int main() { + ADD_FRAME(MAIN_LINE); +#if USE_FORK_AND_SIGACTION + // Set and reset a SEGV handler. If successful, trigger a SIGSEGV + // and obtain a stack trace in the handler. Otherwise simply print + // one near the point it would have been generated. + trigger_segv = maybe_setup_segv_handler(true); + if (trigger_segv) { + if (const pid_t childid = fork()) { + // The child process that prints the expected stack trace + // (the one printed by the program must run first so that + // the CHECK statements below can capture the PC values + // first and then use them for comparison to the values + // printed in the stack trace printed by the sanitizer + // in the parent. + puts("parent waiting..."); + waitpid(childid, 0, 0); + puts("parent continuing..."); + } else { + puts("child running..."); + maybe_setup_segv_handler(false); + } + } +#endif // USE_FORK_AND_SIGACTION +#line MAIN_LINE + bar(0); +} + +// Checks for Linux (GLIBC, really, or those where USE_FORK_AND_SIGACTION +// is defined to 1): +// Check the stack trace generated by __builtin_return_address... +// CHECK-Linux: >0 [[PCFOO:0x[0-9a-f]+]] foo:101 +// CHECK-NEXT-Linux: >1 [[PCBAR:0x[0-9a-f]+]] bar:201 +// CHECK-NEXT-Linux: >2 [[PCMAIN:0x[0-9a-f]+]] main:301 + +// ...against stack trace generated by __sanitizer_print_stack_trace, +// verifying that PC values and line numbers printed by both match. +// CHECK-Linux: #0 [[PCFOO]] foo(int*):101 +// CHECK-NEXT-Linux: #1 [[PCBAR]] bar(int*):201 +// CHECK-NEXT-Linux: #2 [[PCMAIN]] main:301 + +// Checks for systems where USE_FORK_AND_SIGACTION is defined to 0: +// Check the stack trace generated by __builtin_return_address... +// CHECK-other: >0 0 foo:101 +// CHECK-NEXT-other: >1 [[PCBAR:0x[0-9a-f]+]] bar:201 +// CHECK-NEXT-other: >2 [[PCMAIN:0x[0-9a-f]+]] main:301 + +// ...against stack trace generated by __sanitizer_print_stack_trace, +// verifying that PC values and line numbers printed by both match. +// CHECK-other: #1 {{0x[0-9a-f]+}} foo(int*):101 +// CHECK-NEXT-other: #2 [[PCBAR]] bar(int*):201 +// CHECK-NEXT-other: #3 [[PCMAIN]] main:301 Index: test/sanitizer_common/TestCases/print-stack-trace-pc.cc =================================================================== --- test/sanitizer_common/TestCases/print-stack-trace-pc.cc +++ test/sanitizer_common/TestCases/print-stack-trace-pc.cc @@ -0,0 +1,64 @@ +// RUN: %clangxx -O0 %s -o %t && %tool_options='stack_trace_format="#%%n %%p %%f:%%l"' %run %t 2>&1 | FileCheck %s +// RUN: %clangxx -O3 %s -o %t && %tool_options='stack_trace_format="#%%n %%p %%f:%%l"' %run %t 2>&1 | FileCheck %s + +#include + +#include +#include + +static struct StackFrame { + const void *pc; + const char *func; + int line; +} stack[4], *pfrm = stack; + +// Line numbers to include in stack trace. +#define FOO_LINE 101 +#define BAR_LINE 201 +#define MAIN_LINE 301 + +#define ADD_FRAME(line) \ + *pfrm++ = (StackFrame) { __builtin_return_address(0), __func__, line } + +void __attribute__((weak)) foo() { + ADD_FRAME(FOO_LINE); + + for (int i = 0; pfrm-- != stack; ++i) { + const size_t pc = i ? reinterpret_cast(pfrm[1].pc) : 0; + fprintf(stderr, ">%i %#zx %s:%i\n", i, pc, pfrm->func, pfrm->line); + } + fputc('\n', stderr); +#line FOO_LINE + __sanitizer_print_stack_trace(); + + // Avoid printing the same stack trace more than once. + exit(0); +} + +void __attribute__((weak)) bar() { + ADD_FRAME(BAR_LINE); +#line BAR_LINE + foo(); + + // Call foo again to prevent tail call optimization from eliminating + // bar's stack frame from the trace. + foo(); +} + +int main(void) { + ADD_FRAME(MAIN_LINE); +#line MAIN_LINE + bar(); +} + +// Check the stack trace generated by __builtin_return_address... +// CHECK: {{>0 0 foo:101}} +// CHECK-NEXT: >1 [[PCBAR:0x[0-9a-f]*]] bar:201 +// CHECK-NEXT: >2 [[PCMAIN:0x[0-9a-f]*]] main:301 + +// ...against stack trace generated by __sanitizer_print_stack_trace, +// verifying that PC values and line numbers printed by both match. +// CHECK: {{#0 0x[0-9a-f]+ __sanitizer_print_stack_trace:[1-9][0-9]*}} +// CHECK-NEXT: {{#1 0x[0-9a-f]+ foo\(\):101}} +// CHECK-NEXT: #2 [[PCBAR]] bar():201 +// CHECK-NEXT: #3 [[PCMAIN]] main:301