[sanitizer] Use system unwinder in signal handlers on Android.

Because of the way Bionic sets up signal stack frames, libc unwinder is unable
to step through it, resulting in broken SEGV stack traces.

Luckily, libcorkscrew.so on Android implements an unwinder that can start with
a signal context, thus sidestepping the issue.

llvm-svn: 201151
This commit is contained in:
Evgeniy Stepanov 2014-02-11 13:38:57 +00:00
parent ca183eed55
commit 769d46f373
11 changed files with 111 additions and 26 deletions

View File

@ -38,7 +38,7 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
if (13 != internal_write(2, "ASAN:SIGSEGV\n", 13)) Die();
uptr pc, sp, bp;
GetPcSpBp(context, &pc, &sp, &bp);
ReportSIGSEGV(pc, sp, bp, addr);
ReportSIGSEGV(pc, sp, bp, context, addr);
}
// ---------------------- TSD ---------------- {{{1

View File

@ -570,7 +570,7 @@ class ScopedInErrorReport {
}
};
void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@ -579,7 +579,7 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) {
(void*)addr, (void*)pc, (void*)sp, (void*)bp,
GetCurrentTidOrInvalid());
Printf("%s", d.EndWarning());
GET_STACK_TRACE_FATAL(pc, bp);
GET_STACK_TRACE_SIGNAL(pc, bp, context);
stack.Print();
Printf("AddressSanitizer can not provide additional info.\n");
ReportErrorSummary("SEGV", &stack);

View File

@ -32,7 +32,8 @@ void DescribeAddress(uptr addr, uptr access_size);
void DescribeThread(AsanThreadContext *context);
// Different kinds of error reports.
void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr);
void NORETURN
ReportSIGSEGV(uptr pc, uptr sp, uptr bp, void *context, uptr addr);
void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,

View File

@ -532,6 +532,7 @@ static void AsanInitInternal() {
SetCurrentThread(main_thread);
main_thread->ThreadStart(internal_getpid());
force_interface_symbols(); // no-op.
SanitizerInitializeUnwinder();
#if CAN_SANITIZE_LEAKS
__lsan::InitCommonLsan();

View File

@ -23,11 +23,11 @@
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
#if SANITIZER_WINDOWS
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
StackTrace stack; \
stack.Unwind(max_s, pc, bp, 0, 0, fast)
#define GET_STACK_TRACE_WITH_PC_BP_AND_CONTEXT(max_s, pc, bp, context, fast) \
StackTrace stack; \
stack.Unwind(max_s, pc, bp, context, 0, 0, fast)
#else
#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \
#define GET_STACK_TRACE_WITH_PC_BP_AND_CONTEXT(max_s, pc, bp, context, fast) \
StackTrace stack; \
{ \
AsanThread *t; \
@ -37,10 +37,10 @@
uptr stack_top = t->stack_top(); \
uptr stack_bottom = t->stack_bottom(); \
ScopedUnwinding unwind_scope(t); \
stack.Unwind(max_s, pc, bp, stack_top, stack_bottom, fast); \
stack.Unwind(max_s, pc, bp, context, stack_top, stack_bottom, fast); \
} else if (t == 0 && !fast) { \
/* If GetCurrentThread() has failed, try to do slow unwind anyways. */ \
stack.Unwind(max_s, pc, bp, 0, 0, false); \
stack.Unwind(max_s, pc, bp, context, 0, 0, false); \
} \
} \
}
@ -50,13 +50,17 @@
// as early as possible (in functions exposed to the user), as we generally
// don't want stack trace to contain functions from ASan internals.
#define GET_STACK_TRACE(max_size, fast) \
GET_STACK_TRACE_WITH_PC_AND_BP(max_size, \
StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), fast)
#define GET_STACK_TRACE(max_size, fast) \
GET_STACK_TRACE_WITH_PC_BP_AND_CONTEXT(max_size, StackTrace::GetCurrentPc(), \
GET_CURRENT_FRAME(), 0, fast)
#define GET_STACK_TRACE_FATAL(pc, bp) \
GET_STACK_TRACE_WITH_PC_AND_BP(kStackTraceMax, pc, bp, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_FATAL(pc, bp) \
GET_STACK_TRACE_WITH_PC_BP_AND_CONTEXT(kStackTraceMax, pc, bp, 0, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_SIGNAL(pc, bp, context) \
GET_STACK_TRACE_WITH_PC_BP_AND_CONTEXT(kStackTraceMax, pc, bp, context, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_FATAL_HERE \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)

View File

@ -154,11 +154,11 @@ void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp,
if (!StackTrace::WillUseFastUnwind(request_fast_unwind)) {
// Block reports from our interceptors during _Unwind_Backtrace.
SymbolizerScope sym_scope;
return stack->Unwind(max_s, pc, bp, 0, 0, request_fast_unwind);
return stack->Unwind(max_s, pc, bp, 0, 0, 0, request_fast_unwind);
}
uptr stack_bottom = msan_stack_bounds.stack_addr;
uptr stack_top = stack_bottom + msan_stack_bounds.stack_size;
stack->Unwind(max_s, pc, bp, stack_top, stack_bottom, request_fast_unwind);
stack->Unwind(max_s, pc, bp, 0, stack_top, stack_bottom, request_fast_unwind);
}
void PrintWarning(uptr pc, uptr bp) {

View File

@ -509,9 +509,11 @@ F IndirectExternCall(F f) {
#if SANITIZER_ANDROID
void AndroidLogWrite(const char *buffer);
void GetExtraActivationFlags(char *buf, uptr size);
void SanitizerInitializeUnwinder();
#else
INLINE void AndroidLogWrite(const char *buffer_unused) {}
INLINE void GetExtraActivationFlags(char *buf, uptr size) { *buf = '\0'; }
INLINE void SanitizerInitializeUnwinder() {}
#endif
} // namespace __sanitizer

View File

@ -142,6 +142,51 @@ bool SanitizerGetThreadName(char *name, int max_len) {
#ifndef SANITIZER_GO
//------------------------- SlowUnwindStack -----------------------------------
typedef struct {
uptr absolute_pc;
uptr stack_top;
uptr stack_size;
} backtrace_frame_t;
extern "C" {
typedef void *(*acquire_my_map_info_list_func)();
typedef void (*release_my_map_info_list_func)(void *map);
typedef sptr (*unwind_backtrace_signal_arch_func)(
void *siginfo, void *sigcontext, void *map_info_list,
backtrace_frame_t *backtrace, uptr ignore_depth, uptr max_depth);
acquire_my_map_info_list_func acquire_my_map_info_list;
release_my_map_info_list_func release_my_map_info_list;
unwind_backtrace_signal_arch_func unwind_backtrace_signal_arch;
} // extern "C"
#if SANITIZER_ANDROID
void SanitizerInitializeUnwinder() {
void *p = dlopen("libcorkscrew.so", RTLD_LAZY);
if (!p) {
VReport(1,
"Failed to open libcorkscrew.so. You may see broken stack traces "
"in SEGV reports.");
return;
}
acquire_my_map_info_list =
(acquire_my_map_info_list_func)(uptr)dlsym(p, "acquire_my_map_info_list");
release_my_map_info_list =
(release_my_map_info_list_func)(uptr)dlsym(p, "release_my_map_info_list");
unwind_backtrace_signal_arch = (unwind_backtrace_signal_arch_func)(uptr)dlsym(
p, "unwind_backtrace_signal_arch");
if (!acquire_my_map_info_list || !release_my_map_info_list ||
!unwind_backtrace_signal_arch) {
VReport(1,
"Failed to find one of the required symbols in libcorkscrew.so. "
"You may see broken stack traces in SEGV reports.");
acquire_my_map_info_list = NULL;
unwind_backtrace_signal_arch = NULL;
release_my_map_info_list = NULL;
}
}
#endif
#ifdef __arm__
#define UNWIND_STOP _URC_END_OF_STACK
#define UNWIND_CONTINUE _URC_NO_REASON
@ -192,6 +237,31 @@ void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
trace[0] = pc;
}
void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth) {
if (!unwind_backtrace_signal_arch) {
SlowUnwindStack(pc, max_depth);
return;
}
size = 0;
if (max_depth == 0) return;
void *map = acquire_my_map_info_list();
CHECK(map);
backtrace_frame_t frames[kStackTraceMax];
// siginfo argument appears to be unused.
sptr res =
unwind_backtrace_signal_arch(/* siginfo */ NULL, context, map, frames,
/* ignore_depth */ 0, max_depth);
release_my_map_info_list(map);
if (res < 0) return;
CHECK((uptr)res <= kStackTraceMax);
for (sptr i = 0; i < res; ++i)
trace[size++] = frames[i].absolute_pc;
}
#endif // !SANITIZER_GO
static uptr g_tls_size;

View File

@ -58,7 +58,7 @@ struct StackTrace {
return request_fast_unwind;
}
void Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top,
void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
uptr stack_bottom, bool request_fast_unwind);
static uptr GetCurrentPc();
@ -68,6 +68,8 @@ struct StackTrace {
void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
uptr max_depth);
void SlowUnwindStack(uptr pc, uptr max_depth);
void SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth);
void PopStackFrames(uptr count);
uptr LocatePcInTrace(uptr pc);
};

View File

@ -63,12 +63,17 @@ void StackTrace::PrintStack(const uptr *addr, uptr size) {
Printf("\n");
}
void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top,
uptr stack_bottom, bool request_fast_unwind) {
if (!WillUseFastUnwind(request_fast_unwind))
SlowUnwindStack(pc, max_depth);
else
void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
uptr stack_top, uptr stack_bottom,
bool request_fast_unwind) {
if (!WillUseFastUnwind(request_fast_unwind)) {
if (context)
SlowUnwindStackWithContext(pc, context, max_depth);
else
SlowUnwindStack(pc, max_depth);
} else {
FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth);
}
top_frame_bp = size ? bp : 0;
}

View File

@ -713,8 +713,8 @@ void PrintCurrentStackSlow() {
#ifndef TSAN_GO
__sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace,
sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace;
ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(),
0, 0, 0, false);
ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), 0, 0,
0, 0, false);
for (uptr i = 0; i < ptrace->size / 2; i++) {
uptr tmp = ptrace->trace[i];
ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1];