diff options
Diffstat (limited to 'lib/asan/asan_report.cc')
-rw-r--r-- | lib/asan/asan_report.cc | 196 |
1 files changed, 116 insertions, 80 deletions
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc index aeeebf452ca88..ed4e433c7a542 100644 --- a/lib/asan/asan_report.cc +++ b/lib/asan/asan_report.cc @@ -20,6 +20,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" namespace __asan { @@ -44,15 +45,6 @@ void AppendToErrorMessageBuffer(const char *buffer) { } // ---------------------- Decorator ------------------------------ {{{1 -bool PrintsToTtyCached() { - static int cached = 0; - static bool prints_to_tty; - if (!cached) { // Ok wrt threads since we are printing only from one thread. - prints_to_tty = PrintsToTty(); - cached = 1; - } - return prints_to_tty; -} class Decorator: private __sanitizer::AnsiColorDecorator { public: Decorator() : __sanitizer::AnsiColorDecorator(PrintsToTtyCached()) { } @@ -113,7 +105,7 @@ static void PrintShadowBytes(const char *before, u8 *bytes, for (uptr i = 0; i < n; i++) { u8 *p = bytes + i; const char *before = p == guilty ? "[" : - p - 1 == guilty ? "" : " "; + (p - 1 == guilty && i != 0) ? "" : " "; const char *after = p == guilty ? "]" : ""; PrintShadowByte(before, *p, after); } @@ -125,7 +117,7 @@ static void PrintLegend() { "application bytes):\n", (int)SHADOW_GRANULARITY); PrintShadowByte(" Addressable: ", 0); Printf(" Partially addressable: "); - for (uptr i = 1; i < SHADOW_GRANULARITY; i++) + for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte("", i, " "); Printf("\n"); PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); @@ -175,6 +167,11 @@ static void PrintZoneForPointer(uptr ptr, uptr zone_ptr, } } +static void DescribeThread(AsanThread *t) { + if (t) + DescribeThread(t->context()); +} + // ---------------------- Address Descriptions ------------------- {{{1 static bool IsASCII(unsigned char c) { @@ -184,7 +181,9 @@ static bool IsASCII(unsigned char c) { static const char *MaybeDemangleGlobalName(const char *name) { // We can spoil names of globals with C linkage, so use an heuristic // approach to check if the name should be demangled. - return (name[0] == '_' && name[1] == 'Z') ? Demangle(name) : name; + return (name[0] == '_' && name[1] == 'Z') + ? Symbolizer::Get()->Demangle(name) + : name; } // Check if the global is a zero-terminated ASCII string. If so, print it. @@ -264,10 +263,53 @@ const char *ThreadNameWithParenthesis(u32 tid, char buff[], return ThreadNameWithParenthesis(t, buff, buff_len); } +void PrintAccessAndVarIntersection(const char *var_name, + uptr var_beg, uptr var_size, + uptr addr, uptr access_size, + uptr prev_var_end, uptr next_var_beg) { + uptr var_end = var_beg + var_size; + uptr addr_end = addr + access_size; + const char *pos_descr = 0; + // If the variable [var_beg, var_end) is the nearest variable to the + // current memory access, indicate it in the log. + if (addr >= var_beg) { + if (addr_end <= var_end) + pos_descr = "is inside"; // May happen if this is a use-after-return. + else if (addr < var_end) + pos_descr = "partially overflows"; + else if (addr_end <= next_var_beg && + next_var_beg - addr_end >= addr - var_end) + pos_descr = "overflows"; + } else { + if (addr_end > var_beg) + pos_descr = "partially underflows"; + else if (addr >= prev_var_end && + addr - prev_var_end >= var_beg - addr_end) + pos_descr = "underflows"; + } + Printf(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name); + if (pos_descr) { + Decorator d; + // FIXME: we may want to also print the size of the access here, + // but in case of accesses generated by memset it may be confusing. + Printf("%s <== Memory access at offset %zd %s this variable%s\n", + d.Location(), addr, pos_descr, d.EndLocation()); + } else { + Printf("\n"); + } +} + +struct StackVarDescr { + uptr beg; + uptr size; + const char *name_pos; + uptr name_len; +}; + bool DescribeAddressIfStack(uptr addr, uptr access_size) { AsanThread *t = FindThreadByStackAddress(addr); if (!t) return false; - const sptr kBufSize = 4095; + const uptr kBufSize = 4095; char buf[kBufSize]; uptr offset = 0; uptr frame_pc = 0; @@ -306,31 +348,44 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) { PrintStack(&alloca_stack); // Report the number of stack objects. char *p; - uptr n_objects = internal_simple_strtoll(frame_descr, &p, 10); + uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10); CHECK_GT(n_objects, 0); Printf(" This frame has %zu object(s):\n", n_objects); + // Report all objects in this frame. + InternalScopedBuffer<StackVarDescr> vars(n_objects); for (uptr i = 0; i < n_objects; i++) { uptr beg, size; - sptr len; - beg = internal_simple_strtoll(p, &p, 10); - size = internal_simple_strtoll(p, &p, 10); - len = internal_simple_strtoll(p, &p, 10); - if (beg <= 0 || size <= 0 || len < 0 || *p != ' ') { + uptr len; + beg = (uptr)internal_simple_strtoll(p, &p, 10); + size = (uptr)internal_simple_strtoll(p, &p, 10); + len = (uptr)internal_simple_strtoll(p, &p, 10); + if (beg == 0 || size == 0 || *p != ' ') { Printf("AddressSanitizer can't parse the stack frame " "descriptor: |%s|\n", frame_descr); break; } p++; - buf[0] = 0; - internal_strncat(buf, p, Min(kBufSize, len)); + vars[i].beg = beg; + vars[i].size = size; + vars[i].name_pos = p; + vars[i].name_len = len; p += len; - Printf(" [%zu, %zu) '%s'\n", beg, beg + size, buf); + } + for (uptr i = 0; i < n_objects; i++) { + buf[0] = 0; + internal_strncat(buf, vars[i].name_pos, + static_cast<uptr>(Min(kBufSize, vars[i].name_len))); + uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0; + uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL); + PrintAccessAndVarIntersection(buf, vars[i].beg, vars[i].size, + offset, access_size, + prev_var_end, next_var_beg); } Printf("HINT: this may be a false positive if your program uses " "some custom stack unwind mechanism or swapcontext\n" " (longjmp and C++ exceptions *are* supported)\n"); - DescribeThread(t->context()); + DescribeThread(t); return true; } @@ -360,7 +415,11 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); - if (!chunk.IsValid()) return; + if (!chunk.IsValid()) { + Printf("AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); + return; + } DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); asanThreadRegistry().CheckLocked(); @@ -368,13 +427,11 @@ void DescribeHeapAddress(uptr addr, uptr access_size) { GetThreadContextByTidLocked(chunk.AllocTid()); StackTrace alloc_stack; chunk.GetAllocStack(&alloc_stack); - AsanThread *t = GetCurrentThread(); - CHECK(t); char tname[128]; Decorator d; + AsanThreadContext *free_thread = 0; if (chunk.FreeTid() != kInvalidTid) { - AsanThreadContext *free_thread = - GetThreadContextByTidLocked(chunk.FreeTid()); + free_thread = GetThreadContextByTidLocked(chunk.FreeTid()); Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(), free_thread->tid, ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)), @@ -386,19 +443,17 @@ void DescribeHeapAddress(uptr addr, uptr access_size) { d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->context()); - DescribeThread(free_thread); - DescribeThread(alloc_thread); } else { Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(), alloc_thread->tid, ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)), d.EndAllocation()); - PrintStack(&alloc_stack); - DescribeThread(t->context()); - DescribeThread(alloc_thread); } + PrintStack(&alloc_stack); + DescribeThread(GetCurrentThread()); + if (free_thread) + DescribeThread(free_thread); + DescribeThread(alloc_thread); } void DescribeAddress(uptr addr, uptr access_size) { @@ -431,7 +486,9 @@ void DescribeThread(AsanThreadContext *context) { context->parent_tid, ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - PrintStack(&context->stack); + uptr stack_size; + const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size); + PrintStack(stack_trace, stack_size); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { AsanThreadContext *parent_context = @@ -476,21 +533,11 @@ class ScopedInErrorReport { reporting_thread_tid = GetCurrentTidOrInvalid(); Printf("====================================================" "=============\n"); - if (reporting_thread_tid != kInvalidTid) { - // We started reporting an error message. Stop using the fake stack - // in case we call an instrumented function from a symbolizer. - AsanThread *curr_thread = GetCurrentThread(); - CHECK(curr_thread); - curr_thread->fake_stack().StopUsingFakeStack(); - } } // Destructor is NORETURN, as functions that report errors are. NORETURN ~ScopedInErrorReport() { // Make sure the current thread is announced. - AsanThread *curr_thread = GetCurrentThread(); - if (curr_thread) { - DescribeThread(curr_thread->context()); - } + DescribeThread(GetCurrentThread()); // Print memory stats. if (flags()->print_stats) __asan_print_accumulated_stats(); @@ -502,22 +549,6 @@ class ScopedInErrorReport { } }; -static void ReportSummary(const char *error_type, StackTrace *stack) { - if (!stack->size) return; - if (IsSymbolizerAvailable()) { - AddressInfo ai; - // Currently, we include the first stack frame into the report summary. - // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - SymbolizeCode(pc, &ai, 1); - ReportErrorSummary(error_type, - StripPathPrefix(ai.file, - common_flags()->strip_path_prefix), - ai.line, ai.function); - } - // FIXME: do we need to print anything at all if there is no symbolizer? -} - void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { ScopedInErrorReport in_report; Decorator d; @@ -527,13 +558,13 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { (void*)addr, (void*)pc, (void*)sp, (void*)bp, GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); - Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); PrintStack(&stack); - ReportSummary("SEGV", &stack); + Printf("AddressSanitizer can not provide additional info.\n"); + ReportErrorSummary("SEGV", &stack); } -void ReportDoubleFree(uptr addr, StackTrace *stack) { +void ReportDoubleFree(uptr addr, StackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); @@ -543,14 +574,15 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) { "thread T%d%s:\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("double-free", stack); + ReportErrorSummary("double-free", &stack); } -void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { +void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); @@ -560,12 +592,14 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { "which was not malloc()-ed: %p in thread T%d%s\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-free", stack); + ReportErrorSummary("bad-free", &stack); } -void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, +void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, AllocType alloc_type, AllocType dealloc_type) { static const char *alloc_names[] = @@ -579,9 +613,11 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", alloc_names[alloc_type], dealloc_names[dealloc_type], addr); Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("alloc-dealloc-mismatch", stack); + ReportErrorSummary("alloc-dealloc-mismatch", &stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } @@ -596,7 +632,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-malloc_usable_size", stack); + ReportErrorSummary("bad-malloc_usable_size", stack); } void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { @@ -609,7 +645,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-__asan_get_allocated_size", stack); + ReportErrorSummary("bad-__asan_get_allocated_size", stack); } void ReportStringFunctionMemoryRangesOverlap( @@ -627,7 +663,7 @@ void ReportStringFunctionMemoryRangesOverlap( PrintStack(stack); DescribeAddress((uptr)offset1, length1); DescribeAddress((uptr)offset2, length2); - ReportSummary(bug_type, stack); + ReportErrorSummary(bug_type, stack); } // ----------------------- Mac-specific reports ----------------- {{{1 @@ -737,7 +773,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, PrintStack(&stack); DescribeAddress(addr, access_size); - ReportSummary(bug_descr, &stack); + ReportErrorSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } @@ -758,6 +794,6 @@ void __asan_describe_address(uptr addr) { #if !SANITIZER_SUPPORTS_WEAK_HOOKS // Provide default implementation of __asan_on_error that does nothing // and may be overriden by user. -SANITIZER_WEAK_ATTRIBUTE SANITIZER_INTERFACE_ATTRIBUTE NOINLINE +SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE void __asan_on_error() {} #endif |