summaryrefslogtreecommitdiff
path: root/lib/asan/asan_report.cc
diff options
context:
space:
mode:
Diffstat (limited to 'lib/asan/asan_report.cc')
-rw-r--r--lib/asan/asan_report.cc196
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