2012-08-09 15:40:58 +08:00
|
|
|
//===-- asan_report.cc ----------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// The LLVM Compiler Infrastructure
|
|
|
|
//
|
|
|
|
// This file is distributed under the University of Illinois Open Source
|
|
|
|
// License. See LICENSE.TXT for details.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// This file is a part of AddressSanitizer, an address sanity checker.
|
|
|
|
//
|
|
|
|
// This file contains error reporting code.
|
|
|
|
//===----------------------------------------------------------------------===//
|
2015-10-01 08:22:21 +08:00
|
|
|
|
2012-08-10 23:13:05 +08:00
|
|
|
#include "asan_flags.h"
|
2012-08-09 15:40:58 +08:00
|
|
|
#include "asan_internal.h"
|
2012-08-09 17:06:52 +08:00
|
|
|
#include "asan_mapping.h"
|
2012-08-09 15:40:58 +08:00
|
|
|
#include "asan_report.h"
|
2016-02-09 03:21:08 +08:00
|
|
|
#include "asan_scariness_score.h"
|
2012-08-09 15:40:58 +08:00
|
|
|
#include "asan_stack.h"
|
2012-08-09 17:27:24 +08:00
|
|
|
#include "asan_thread.h"
|
2012-12-18 15:32:16 +08:00
|
|
|
#include "sanitizer_common/sanitizer_common.h"
|
2013-05-06 19:27:58 +08:00
|
|
|
#include "sanitizer_common/sanitizer_flags.h"
|
2012-12-18 15:32:16 +08:00
|
|
|
#include "sanitizer_common/sanitizer_report_decorator.h"
|
2013-10-18 22:50:44 +08:00
|
|
|
#include "sanitizer_common/sanitizer_stackdepot.h"
|
2012-12-26 22:44:46 +08:00
|
|
|
#include "sanitizer_common/sanitizer_symbolizer.h"
|
2012-08-09 15:40:58 +08:00
|
|
|
|
|
|
|
namespace __asan {
|
|
|
|
|
2012-08-13 19:23:40 +08:00
|
|
|
// -------------------- User-specified callbacks ----------------- {{{1
|
2012-08-09 18:56:57 +08:00
|
|
|
static void (*error_report_callback)(const char*);
|
2015-10-01 08:22:21 +08:00
|
|
|
static char *error_message_buffer = nullptr;
|
2012-08-09 18:56:57 +08:00
|
|
|
static uptr error_message_buffer_pos = 0;
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
static BlockingMutex error_message_buf_mutex(LINKER_INITIALIZED);
|
2015-12-10 16:08:53 +08:00
|
|
|
static const unsigned kAsanBuggyPcPoolSize = 25;
|
2015-12-10 19:07:19 +08:00
|
|
|
static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
|
2012-08-09 18:56:57 +08:00
|
|
|
|
2014-09-27 03:15:32 +08:00
|
|
|
struct ReportData {
|
|
|
|
uptr pc;
|
|
|
|
uptr sp;
|
|
|
|
uptr bp;
|
|
|
|
uptr addr;
|
|
|
|
bool is_write;
|
|
|
|
uptr access_size;
|
|
|
|
const char *description;
|
|
|
|
};
|
|
|
|
|
|
|
|
static bool report_happened = false;
|
|
|
|
static ReportData report_data = {};
|
|
|
|
|
2012-08-09 18:56:57 +08:00
|
|
|
void AppendToErrorMessageBuffer(const char *buffer) {
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
BlockingMutexLock l(&error_message_buf_mutex);
|
|
|
|
if (!error_message_buffer) {
|
|
|
|
error_message_buffer =
|
2015-11-21 02:42:01 +08:00
|
|
|
(char*)MmapOrDieQuietly(kErrorMessageBufferSize, __func__);
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
error_message_buffer_pos = 0;
|
2012-08-09 18:56:57 +08:00
|
|
|
}
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
uptr length = internal_strlen(buffer);
|
2015-11-21 02:42:01 +08:00
|
|
|
RAW_CHECK(kErrorMessageBufferSize >= error_message_buffer_pos);
|
|
|
|
uptr remaining = kErrorMessageBufferSize - error_message_buffer_pos;
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
internal_strncpy(error_message_buffer + error_message_buffer_pos,
|
|
|
|
buffer, remaining);
|
2015-11-21 02:42:01 +08:00
|
|
|
error_message_buffer[kErrorMessageBufferSize - 1] = '\0';
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
// FIXME: reallocate the buffer instead of truncating the message.
|
|
|
|
error_message_buffer_pos += Min(remaining, length);
|
2012-08-09 18:56:57 +08:00
|
|
|
}
|
|
|
|
|
2012-12-18 15:32:16 +08:00
|
|
|
// ---------------------- Decorator ------------------------------ {{{1
|
2014-02-14 16:59:42 +08:00
|
|
|
class Decorator: public __sanitizer::SanitizerCommonDecorator {
|
2012-12-18 15:32:16 +08:00
|
|
|
public:
|
2014-02-14 16:59:42 +08:00
|
|
|
Decorator() : SanitizerCommonDecorator() { }
|
2012-12-18 15:32:16 +08:00
|
|
|
const char *Access() { return Blue(); }
|
|
|
|
const char *EndAccess() { return Default(); }
|
|
|
|
const char *Location() { return Green(); }
|
|
|
|
const char *EndLocation() { return Default(); }
|
|
|
|
const char *Allocation() { return Magenta(); }
|
|
|
|
const char *EndAllocation() { return Default(); }
|
2012-12-19 17:53:32 +08:00
|
|
|
|
|
|
|
const char *ShadowByte(u8 byte) {
|
|
|
|
switch (byte) {
|
|
|
|
case kAsanHeapLeftRedzoneMagic:
|
|
|
|
case kAsanHeapRightRedzoneMagic:
|
2014-08-04 20:43:13 +08:00
|
|
|
case kAsanArrayCookieMagic:
|
2012-12-19 17:53:32 +08:00
|
|
|
return Red();
|
|
|
|
case kAsanHeapFreeMagic:
|
|
|
|
return Magenta();
|
|
|
|
case kAsanStackLeftRedzoneMagic:
|
|
|
|
case kAsanStackMidRedzoneMagic:
|
|
|
|
case kAsanStackRightRedzoneMagic:
|
|
|
|
case kAsanStackPartialRedzoneMagic:
|
|
|
|
return Red();
|
|
|
|
case kAsanStackAfterReturnMagic:
|
|
|
|
return Magenta();
|
|
|
|
case kAsanInitializationOrderMagic:
|
|
|
|
return Cyan();
|
|
|
|
case kAsanUserPoisonedMemoryMagic:
|
2013-11-19 16:40:07 +08:00
|
|
|
case kAsanContiguousContainerOOBMagic:
|
2014-11-21 18:32:05 +08:00
|
|
|
case kAsanAllocaLeftMagic:
|
|
|
|
case kAsanAllocaRightMagic:
|
2012-12-19 17:53:32 +08:00
|
|
|
return Blue();
|
|
|
|
case kAsanStackUseAfterScopeMagic:
|
|
|
|
return Magenta();
|
|
|
|
case kAsanGlobalRedzoneMagic:
|
|
|
|
return Red();
|
|
|
|
case kAsanInternalHeapMagic:
|
|
|
|
return Yellow();
|
2014-10-17 09:22:37 +08:00
|
|
|
case kAsanIntraObjectRedzone:
|
|
|
|
return Yellow();
|
2012-12-19 17:53:32 +08:00
|
|
|
default:
|
|
|
|
return Default();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const char *EndShadowByte() { return Default(); }
|
2014-09-22 19:58:52 +08:00
|
|
|
const char *MemoryByte() { return Magenta(); }
|
|
|
|
const char *EndMemoryByte() { return Default(); }
|
2012-12-18 15:32:16 +08:00
|
|
|
};
|
|
|
|
|
2012-08-10 23:13:05 +08:00
|
|
|
// ---------------------- Helper functions ----------------------- {{{1
|
|
|
|
|
2014-09-22 19:58:52 +08:00
|
|
|
static void PrintMemoryByte(InternalScopedString *str, const char *before,
|
|
|
|
u8 byte, bool in_shadow, const char *after = "\n") {
|
2012-12-19 17:53:32 +08:00
|
|
|
Decorator d;
|
2014-09-22 19:58:52 +08:00
|
|
|
str->append("%s%s%x%x%s%s", before,
|
|
|
|
in_shadow ? d.ShadowByte(byte) : d.MemoryByte(),
|
|
|
|
byte >> 4, byte & 15,
|
|
|
|
in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintShadowByte(InternalScopedString *str, const char *before,
|
|
|
|
u8 byte, const char *after = "\n") {
|
|
|
|
PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
|
2012-12-19 17:53:32 +08:00
|
|
|
}
|
|
|
|
|
2014-01-23 18:52:33 +08:00
|
|
|
static void PrintShadowBytes(InternalScopedString *str, const char *before,
|
|
|
|
u8 *bytes, u8 *guilty, uptr n) {
|
2012-12-19 17:53:32 +08:00
|
|
|
Decorator d;
|
2014-01-23 18:52:33 +08:00
|
|
|
if (before) str->append("%s%p:", before, bytes);
|
2012-12-19 17:53:32 +08:00
|
|
|
for (uptr i = 0; i < n; i++) {
|
|
|
|
u8 *p = bytes + i;
|
2014-01-23 18:49:47 +08:00
|
|
|
const char *before =
|
|
|
|
p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
|
2012-12-19 17:53:32 +08:00
|
|
|
const char *after = p == guilty ? "]" : "";
|
2014-01-23 18:52:33 +08:00
|
|
|
PrintShadowByte(str, before, *p, after);
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
2014-01-23 18:52:33 +08:00
|
|
|
str->append("\n");
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
|
|
|
|
2014-01-23 18:52:33 +08:00
|
|
|
static void PrintLegend(InternalScopedString *str) {
|
|
|
|
str->append(
|
2014-01-23 18:49:47 +08:00
|
|
|
"Shadow byte legend (one shadow byte represents %d "
|
|
|
|
"application bytes):\n",
|
|
|
|
(int)SHADOW_GRANULARITY);
|
2014-01-23 18:52:33 +08:00
|
|
|
PrintShadowByte(str, " Addressable: ", 0);
|
|
|
|
str->append(" Partially addressable: ");
|
|
|
|
for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
|
|
|
|
str->append("\n");
|
|
|
|
PrintShadowByte(str, " Heap left redzone: ",
|
|
|
|
kAsanHeapLeftRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Heap right redzone: ",
|
|
|
|
kAsanHeapRightRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Freed heap region: ", kAsanHeapFreeMagic);
|
|
|
|
PrintShadowByte(str, " Stack left redzone: ",
|
|
|
|
kAsanStackLeftRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Stack mid redzone: ",
|
|
|
|
kAsanStackMidRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Stack right redzone: ",
|
|
|
|
kAsanStackRightRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Stack partial redzone: ",
|
|
|
|
kAsanStackPartialRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Stack after return: ",
|
|
|
|
kAsanStackAfterReturnMagic);
|
|
|
|
PrintShadowByte(str, " Stack use after scope: ",
|
|
|
|
kAsanStackUseAfterScopeMagic);
|
|
|
|
PrintShadowByte(str, " Global redzone: ", kAsanGlobalRedzoneMagic);
|
|
|
|
PrintShadowByte(str, " Global init order: ",
|
|
|
|
kAsanInitializationOrderMagic);
|
|
|
|
PrintShadowByte(str, " Poisoned by user: ",
|
|
|
|
kAsanUserPoisonedMemoryMagic);
|
2014-04-21 22:18:45 +08:00
|
|
|
PrintShadowByte(str, " Container overflow: ",
|
2013-11-19 16:40:07 +08:00
|
|
|
kAsanContiguousContainerOOBMagic);
|
2014-08-04 20:43:13 +08:00
|
|
|
PrintShadowByte(str, " Array cookie: ",
|
|
|
|
kAsanArrayCookieMagic);
|
2014-10-17 09:22:37 +08:00
|
|
|
PrintShadowByte(str, " Intra object redzone: ",
|
|
|
|
kAsanIntraObjectRedzone);
|
2014-01-23 18:52:33 +08:00
|
|
|
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
|
2014-11-21 18:32:05 +08:00
|
|
|
PrintShadowByte(str, " Left alloca redzone: ", kAsanAllocaLeftMagic);
|
|
|
|
PrintShadowByte(str, " Right alloca redzone: ", kAsanAllocaRightMagic);
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
|
|
|
|
2014-09-22 19:58:52 +08:00
|
|
|
void MaybeDumpInstructionBytes(uptr pc) {
|
|
|
|
if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
|
|
|
|
return;
|
|
|
|
InternalScopedString str(1024);
|
|
|
|
str.append("First 16 instruction bytes at pc: ");
|
|
|
|
if (IsAccessibleMemoryRange(pc, 16)) {
|
|
|
|
for (int i = 0; i < 16; ++i) {
|
|
|
|
PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " ");
|
|
|
|
}
|
|
|
|
str.append("\n");
|
|
|
|
} else {
|
|
|
|
str.append("unaccessible\n");
|
|
|
|
}
|
|
|
|
Report("%s", str.data());
|
|
|
|
}
|
|
|
|
|
2013-01-28 15:34:22 +08:00
|
|
|
static void PrintShadowMemoryForAddress(uptr addr) {
|
2014-01-23 18:49:47 +08:00
|
|
|
if (!AddrIsInMem(addr)) return;
|
2013-01-28 15:34:22 +08:00
|
|
|
uptr shadow_addr = MemToShadow(addr);
|
|
|
|
const uptr n_bytes_per_row = 16;
|
|
|
|
uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
|
2014-01-29 19:12:09 +08:00
|
|
|
InternalScopedString str(4096 * 8);
|
2014-01-23 18:52:33 +08:00
|
|
|
str.append("Shadow bytes around the buggy address:\n");
|
2013-01-28 15:34:22 +08:00
|
|
|
for (int i = -5; i <= 5; i++) {
|
|
|
|
const char *prefix = (i == 0) ? "=>" : " ";
|
2014-01-23 18:52:33 +08:00
|
|
|
PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
|
2014-01-23 18:49:47 +08:00
|
|
|
(u8 *)shadow_addr, n_bytes_per_row);
|
2013-01-28 15:34:22 +08:00
|
|
|
}
|
2014-01-23 18:52:33 +08:00
|
|
|
if (flags()->print_legend) PrintLegend(&str);
|
|
|
|
Printf("%s", str.data());
|
2013-01-28 15:34:22 +08:00
|
|
|
}
|
|
|
|
|
2012-08-10 23:13:05 +08:00
|
|
|
static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
|
|
|
|
const char *zone_name) {
|
|
|
|
if (zone_ptr) {
|
|
|
|
if (zone_name) {
|
2012-08-28 19:34:40 +08:00
|
|
|
Printf("malloc_zone_from_ptr(%p) = %p, which is %s\n",
|
2012-08-10 23:13:05 +08:00
|
|
|
ptr, zone_ptr, zone_name);
|
|
|
|
} else {
|
2012-08-28 19:34:40 +08:00
|
|
|
Printf("malloc_zone_from_ptr(%p) = %p, which doesn't have a name\n",
|
2012-08-10 23:13:05 +08:00
|
|
|
ptr, zone_ptr);
|
|
|
|
}
|
|
|
|
} else {
|
2012-08-28 19:34:40 +08:00
|
|
|
Printf("malloc_zone_from_ptr(%p) = 0\n", ptr);
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-09-10 16:36:21 +08:00
|
|
|
static void DescribeThread(AsanThread *t) {
|
|
|
|
if (t)
|
|
|
|
DescribeThread(t->context());
|
|
|
|
}
|
|
|
|
|
2012-08-09 17:06:52 +08:00
|
|
|
// ---------------------- Address Descriptions ------------------- {{{1
|
|
|
|
|
2012-08-09 17:27:24 +08:00
|
|
|
static bool IsASCII(unsigned char c) {
|
2012-08-10 23:13:05 +08:00
|
|
|
return /*0x00 <= c &&*/ c <= 0x7F;
|
2012-08-09 17:27:24 +08:00
|
|
|
}
|
|
|
|
|
2013-03-27 18:41:22 +08:00
|
|
|
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.
|
2014-05-14 21:55:59 +08:00
|
|
|
bool should_demangle = false;
|
|
|
|
if (name[0] == '_' && name[1] == 'Z')
|
|
|
|
should_demangle = true;
|
|
|
|
else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
|
|
|
|
should_demangle = true;
|
|
|
|
|
2014-09-11 06:45:09 +08:00
|
|
|
return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
|
2013-03-27 18:41:22 +08:00
|
|
|
}
|
|
|
|
|
2013-04-01 16:57:38 +08:00
|
|
|
// Check if the global is a zero-terminated ASCII string. If so, print it.
|
2014-01-23 19:51:03 +08:00
|
|
|
static void PrintGlobalNameIfASCII(InternalScopedString *str,
|
|
|
|
const __asan_global &g) {
|
2013-04-01 16:57:38 +08:00
|
|
|
for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
|
|
|
|
unsigned char c = *(unsigned char*)p;
|
|
|
|
if (c == '\0' || !IsASCII(c)) return;
|
|
|
|
}
|
|
|
|
if (*(char*)(g.beg + g.size - 1) != '\0') return;
|
2014-01-23 19:51:03 +08:00
|
|
|
str->append(" '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
|
|
|
|
(char *)g.beg);
|
2013-04-01 16:57:38 +08:00
|
|
|
}
|
|
|
|
|
2014-07-03 00:54:41 +08:00
|
|
|
static const char *GlobalFilename(const __asan_global &g) {
|
|
|
|
const char *res = g.module_name;
|
|
|
|
// Prefer the filename from source location, if is available.
|
|
|
|
if (g.location)
|
|
|
|
res = g.location->filename;
|
|
|
|
CHECK(res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void PrintGlobalLocation(InternalScopedString *str,
|
|
|
|
const __asan_global &g) {
|
|
|
|
str->append("%s", GlobalFilename(g));
|
|
|
|
if (!g.location)
|
|
|
|
return;
|
|
|
|
if (g.location->line_no)
|
|
|
|
str->append(":%d", g.location->line_no);
|
|
|
|
if (g.location->column_no)
|
|
|
|
str->append(":%d", g.location->column_no);
|
|
|
|
}
|
|
|
|
|
2015-04-23 04:30:15 +08:00
|
|
|
static void DescribeAddressRelativeToGlobal(uptr addr, uptr size,
|
|
|
|
const __asan_global &g) {
|
2014-01-23 19:51:03 +08:00
|
|
|
InternalScopedString str(4096);
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%s", d.Location());
|
2012-08-09 17:27:24 +08:00
|
|
|
if (addr < g.beg) {
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located %zd bytes to the left", (void *)addr,
|
|
|
|
g.beg - addr);
|
2013-02-05 22:32:03 +08:00
|
|
|
} else if (addr + size > g.beg + g.size) {
|
|
|
|
if (addr < g.beg + g.size)
|
|
|
|
addr = g.beg + g.size;
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located %zd bytes to the right", (void *)addr,
|
|
|
|
addr - (g.beg + g.size));
|
2012-08-09 17:27:24 +08:00
|
|
|
} else {
|
2013-02-05 22:32:03 +08:00
|
|
|
// Can it happen?
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
|
2012-08-09 17:27:24 +08:00
|
|
|
}
|
2014-07-03 00:54:41 +08:00
|
|
|
str.append(" of global variable '%s' defined in '",
|
|
|
|
MaybeDemangleGlobalName(g.name));
|
|
|
|
PrintGlobalLocation(&str, g);
|
|
|
|
str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%s", d.EndLocation());
|
|
|
|
PrintGlobalNameIfASCII(&str, g);
|
|
|
|
Printf("%s", str.data());
|
2015-04-23 04:30:15 +08:00
|
|
|
}
|
|
|
|
|
2015-04-23 04:30:19 +08:00
|
|
|
static bool DescribeAddressIfGlobal(uptr addr, uptr size,
|
|
|
|
const char *bug_type) {
|
2015-04-23 04:30:15 +08:00
|
|
|
// Assume address is close to at most four globals.
|
|
|
|
const int kMaxGlobalsInReport = 4;
|
|
|
|
__asan_global globals[kMaxGlobalsInReport];
|
2015-04-23 04:30:19 +08:00
|
|
|
u32 reg_sites[kMaxGlobalsInReport];
|
|
|
|
int globals_num =
|
|
|
|
GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals));
|
2015-04-23 04:30:15 +08:00
|
|
|
if (globals_num == 0)
|
|
|
|
return false;
|
2015-04-23 04:30:19 +08:00
|
|
|
for (int i = 0; i < globals_num; i++) {
|
2015-04-23 04:30:15 +08:00
|
|
|
DescribeAddressRelativeToGlobal(addr, size, globals[i]);
|
2015-04-23 04:30:19 +08:00
|
|
|
if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
|
|
|
|
reg_sites[i]) {
|
|
|
|
Printf(" registered at:\n");
|
|
|
|
StackDepotGet(reg_sites[i]).Print();
|
|
|
|
}
|
|
|
|
}
|
2012-08-09 17:27:24 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2014-09-27 03:15:32 +08:00
|
|
|
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) {
|
2012-08-09 17:06:52 +08:00
|
|
|
if (AddrIsInMem(addr))
|
|
|
|
return false;
|
2014-09-27 03:15:32 +08:00
|
|
|
const char *area_type = nullptr;
|
|
|
|
if (AddrIsInShadowGap(addr)) area_type = "shadow gap";
|
|
|
|
else if (AddrIsInHighShadow(addr)) area_type = "high shadow";
|
|
|
|
else if (AddrIsInLowShadow(addr)) area_type = "low shadow";
|
|
|
|
if (area_type != nullptr) {
|
|
|
|
if (print) {
|
|
|
|
Printf("Address %p is located in the %s area.\n", addr, area_type);
|
|
|
|
} else {
|
|
|
|
CHECK(descr);
|
|
|
|
descr->region_kind = area_type;
|
|
|
|
}
|
2012-08-09 17:06:52 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
CHECK(0 && "Address is not in memory and not in shadow?");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
[asan] Change the way we report the alloca frame on stack-buff-overflow.
Before: the function name was stored by the compiler as a constant string
and the run-time was printing it.
Now: the PC is stored instead and the run-time prints the full symbolized frame.
This adds a couple of instructions into every function with non-empty stack frame,
but also reduces the binary size because we store less strings (I saw 2% size reduction).
This change bumps the asan ABI version to v3.
compiler-rt part, llvm part will follow.
Example of report (now):
==31711==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffa77cf1c5 at pc 0x41feb0 bp 0x7fffa77cefb0 sp 0x7fffa77cefa8
READ of size 1 at 0x7fffa77cf1c5 thread T0
#0 0x41feaf in Frame0(int, char*, char*, char*) stack-oob-frames.cc:20
#1 0x41f7ff in Frame1(int, char*, char*) stack-oob-frames.cc:24
#2 0x41f477 in Frame2(int, char*) stack-oob-frames.cc:28
#3 0x41f194 in Frame3(int) stack-oob-frames.cc:32
#4 0x41eee0 in main stack-oob-frames.cc:38
#5 0x7f0c5566f76c (/lib/x86_64-linux-gnu/libc.so.6+0x2176c)
#6 0x41eb1c (/usr/local/google/kcc/llvm_cmake/a.out+0x41eb1c)
Address 0x7fffa77cf1c5 is located in stack of thread T0 at offset 293 in frame
#0 0x41f87f in Frame0(int, char*, char*, char*) stack-oob-frames.cc:12 <<<<<<<<<<<<<< this is new
This frame has 6 object(s):
[32, 36) 'frame.addr'
[96, 104) 'a.addr'
[160, 168) 'b.addr'
[224, 232) 'c.addr'
[288, 292) 's'
[352, 360) 'd'
llvm-svn: 177723
2013-03-22 18:36:24 +08:00
|
|
|
// Return " (thread_name) " or an empty string if the name is empty.
|
|
|
|
const char *ThreadNameWithParenthesis(AsanThreadContext *t, char buff[],
|
|
|
|
uptr buff_len) {
|
|
|
|
const char *name = t->name;
|
|
|
|
if (name[0] == '\0') return "";
|
|
|
|
buff[0] = 0;
|
|
|
|
internal_strncat(buff, " (", 3);
|
|
|
|
internal_strncat(buff, name, buff_len - 4);
|
|
|
|
internal_strncat(buff, ")", 2);
|
|
|
|
return buff;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *ThreadNameWithParenthesis(u32 tid, char buff[],
|
|
|
|
uptr buff_len) {
|
|
|
|
if (tid == kInvalidTid) return "";
|
|
|
|
asanThreadRegistry().CheckLocked();
|
|
|
|
AsanThreadContext *t = GetThreadContextByTidLocked(tid);
|
|
|
|
return ThreadNameWithParenthesis(t, buff, buff_len);
|
|
|
|
}
|
|
|
|
|
2014-10-02 05:28:54 +08:00
|
|
|
static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
|
|
|
|
uptr access_size, uptr prev_var_end,
|
|
|
|
uptr next_var_beg) {
|
|
|
|
uptr var_end = var.beg + var.size;
|
2013-09-03 21:58:04 +08:00
|
|
|
uptr addr_end = addr + access_size;
|
2015-10-01 08:22:21 +08:00
|
|
|
const char *pos_descr = nullptr;
|
2014-10-02 05:28:54 +08:00
|
|
|
// If the variable [var.beg, var_end) is the nearest variable to the
|
2013-09-03 21:58:04 +08:00
|
|
|
// current memory access, indicate it in the log.
|
2014-10-02 05:28:54 +08:00
|
|
|
if (addr >= var.beg) {
|
2013-09-03 21:58:04 +08:00
|
|
|
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 {
|
2014-10-02 05:28:54 +08:00
|
|
|
if (addr_end > var.beg)
|
2013-09-03 21:58:04 +08:00
|
|
|
pos_descr = "partially underflows";
|
|
|
|
else if (addr >= prev_var_end &&
|
2014-10-02 05:28:54 +08:00
|
|
|
addr - prev_var_end >= var.beg - addr_end)
|
2013-09-03 21:58:04 +08:00
|
|
|
pos_descr = "underflows";
|
|
|
|
}
|
2014-01-23 19:51:03 +08:00
|
|
|
InternalScopedString str(1024);
|
2014-10-02 05:28:54 +08:00
|
|
|
str.append(" [%zd, %zd)", var.beg, var_end);
|
|
|
|
// Render variable name.
|
|
|
|
str.append(" '");
|
|
|
|
for (uptr i = 0; i < var.name_len; ++i) {
|
|
|
|
str.append("%c", var.name_pos[i]);
|
|
|
|
}
|
|
|
|
str.append("'");
|
2013-09-03 21:58:04 +08:00
|
|
|
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.
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%s <== Memory access at offset %zd %s this variable%s\n",
|
|
|
|
d.Location(), addr, pos_descr, d.EndLocation());
|
2013-09-03 21:58:04 +08:00
|
|
|
} else {
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("\n");
|
2013-09-03 21:58:04 +08:00
|
|
|
}
|
2014-01-23 19:51:03 +08:00
|
|
|
Printf("%s", str.data());
|
2013-09-03 21:58:04 +08:00
|
|
|
}
|
|
|
|
|
2014-07-17 08:18:03 +08:00
|
|
|
bool ParseFrameDescription(const char *frame_descr,
|
|
|
|
InternalMmapVector<StackVarDescr> *vars) {
|
2014-10-02 05:13:00 +08:00
|
|
|
CHECK(frame_descr);
|
2014-07-17 08:18:03 +08:00
|
|
|
char *p;
|
2014-10-02 05:13:00 +08:00
|
|
|
// This string is created by the compiler and has the following form:
|
|
|
|
// "n alloc_1 alloc_2 ... alloc_n"
|
|
|
|
// where alloc_i looks like "offset size len ObjectName".
|
2014-07-17 08:18:03 +08:00
|
|
|
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
|
2014-10-02 05:13:00 +08:00
|
|
|
if (n_objects == 0)
|
|
|
|
return false;
|
2014-07-17 08:18:03 +08:00
|
|
|
|
|
|
|
for (uptr i = 0; i < n_objects; i++) {
|
|
|
|
uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
|
|
|
|
uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
|
|
|
|
uptr len = (uptr)internal_simple_strtoll(p, &p, 10);
|
|
|
|
if (beg == 0 || size == 0 || *p != ' ') {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
p++;
|
|
|
|
StackVarDescr var = {beg, size, p, len};
|
|
|
|
vars->push_back(var);
|
|
|
|
p += len;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
2013-09-03 21:58:04 +08:00
|
|
|
|
2012-08-09 17:06:52 +08:00
|
|
|
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
|
2013-03-21 19:23:41 +08:00
|
|
|
AsanThread *t = FindThreadByStackAddress(addr);
|
2012-08-09 17:06:52 +08:00
|
|
|
if (!t) return false;
|
2013-05-22 22:21:34 +08:00
|
|
|
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
2014-10-02 05:13:00 +08:00
|
|
|
char tname[128];
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.Location());
|
2014-10-02 05:13:00 +08:00
|
|
|
Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(),
|
|
|
|
ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)));
|
|
|
|
|
|
|
|
// Try to fetch precise stack frame for this access.
|
|
|
|
AsanThread::StackFrameAccess access;
|
|
|
|
if (!t->GetStackFrameAccessByAddr(addr, &access)) {
|
|
|
|
Printf("%s\n", d.EndLocation());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation());
|
|
|
|
|
[asan] Change the way we report the alloca frame on stack-buff-overflow.
Before: the function name was stored by the compiler as a constant string
and the run-time was printing it.
Now: the PC is stored instead and the run-time prints the full symbolized frame.
This adds a couple of instructions into every function with non-empty stack frame,
but also reduces the binary size because we store less strings (I saw 2% size reduction).
This change bumps the asan ABI version to v3.
compiler-rt part, llvm part will follow.
Example of report (now):
==31711==ERROR: AddressSanitizer: stack-buffer-overflow on address 0x7fffa77cf1c5 at pc 0x41feb0 bp 0x7fffa77cefb0 sp 0x7fffa77cefa8
READ of size 1 at 0x7fffa77cf1c5 thread T0
#0 0x41feaf in Frame0(int, char*, char*, char*) stack-oob-frames.cc:20
#1 0x41f7ff in Frame1(int, char*, char*) stack-oob-frames.cc:24
#2 0x41f477 in Frame2(int, char*) stack-oob-frames.cc:28
#3 0x41f194 in Frame3(int) stack-oob-frames.cc:32
#4 0x41eee0 in main stack-oob-frames.cc:38
#5 0x7f0c5566f76c (/lib/x86_64-linux-gnu/libc.so.6+0x2176c)
#6 0x41eb1c (/usr/local/google/kcc/llvm_cmake/a.out+0x41eb1c)
Address 0x7fffa77cf1c5 is located in stack of thread T0 at offset 293 in frame
#0 0x41f87f in Frame0(int, char*, char*, char*) stack-oob-frames.cc:12 <<<<<<<<<<<<<< this is new
This frame has 6 object(s):
[32, 36) 'frame.addr'
[96, 104) 'a.addr'
[160, 168) 'b.addr'
[224, 232) 'c.addr'
[288, 292) 's'
[352, 360) 'd'
llvm-svn: 177723
2013-03-22 18:36:24 +08:00
|
|
|
// Now we print the frame where the alloca has happened.
|
|
|
|
// We print this frame as a stack trace with one element.
|
|
|
|
// The symbolizer may print more than one frame if inlining was involved.
|
|
|
|
// The frame numbers may be different than those in the stack trace printed
|
|
|
|
// previously. That's unfortunate, but I have no better solution,
|
|
|
|
// especially given that the alloca may be from entirely different place
|
|
|
|
// (e.g. use-after-scope, or different thread's stack).
|
2016-04-27 02:44:13 +08:00
|
|
|
#if SANITIZER_PPC64V1
|
2014-10-16 02:34:04 +08:00
|
|
|
// On PowerPC64 ELFv1, the address of a function actually points to a
|
2014-10-02 05:13:00 +08:00
|
|
|
// three-doubleword data structure with the first field containing
|
|
|
|
// the address of the function's code.
|
|
|
|
access.frame_pc = *reinterpret_cast<uptr *>(access.frame_pc);
|
|
|
|
#endif
|
2014-10-26 11:35:14 +08:00
|
|
|
access.frame_pc += 16;
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndLocation());
|
2014-10-26 11:35:14 +08:00
|
|
|
StackTrace alloca_stack(&access.frame_pc, 1);
|
2013-12-19 19:25:05 +08:00
|
|
|
alloca_stack.Print();
|
2014-07-17 08:18:03 +08:00
|
|
|
|
|
|
|
InternalMmapVector<StackVarDescr> vars(16);
|
2014-10-02 05:13:00 +08:00
|
|
|
if (!ParseFrameDescription(access.frame_descr, &vars)) {
|
2014-07-17 08:18:03 +08:00
|
|
|
Printf("AddressSanitizer can't parse the stack frame "
|
2014-10-02 05:13:00 +08:00
|
|
|
"descriptor: |%s|\n", access.frame_descr);
|
2014-07-17 08:18:03 +08:00
|
|
|
// 'addr' is a stack address, so return true even if we can't parse frame
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
uptr n_objects = vars.size();
|
2012-08-09 17:06:52 +08:00
|
|
|
// Report the number of stack objects.
|
2012-08-28 19:34:40 +08:00
|
|
|
Printf(" This frame has %zu object(s):\n", n_objects);
|
2013-09-03 21:58:04 +08:00
|
|
|
|
2012-08-09 17:06:52 +08:00
|
|
|
// Report all objects in this frame.
|
2013-09-03 22:53:02 +08:00
|
|
|
for (uptr i = 0; i < n_objects; i++) {
|
2013-09-03 21:58:04 +08:00
|
|
|
uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
|
2013-09-03 23:09:21 +08:00
|
|
|
uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
|
2014-10-02 05:28:54 +08:00
|
|
|
PrintAccessAndVarIntersection(vars[i], access.offset, access_size,
|
|
|
|
prev_var_end, next_var_beg);
|
2012-08-09 17:06:52 +08:00
|
|
|
}
|
2012-08-28 19:34:40 +08:00
|
|
|
Printf("HINT: this may be a false positive if your program uses "
|
2014-07-22 21:44:18 +08:00
|
|
|
"some custom stack unwind mechanism or swapcontext\n");
|
|
|
|
if (SANITIZER_WINDOWS)
|
|
|
|
Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");
|
|
|
|
else
|
|
|
|
Printf(" (longjmp and C++ exceptions *are* supported)\n");
|
|
|
|
|
2013-09-10 16:36:21 +08:00
|
|
|
DescribeThread(t);
|
2012-08-09 17:06:52 +08:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2012-09-18 15:38:10 +08:00
|
|
|
static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
|
|
|
|
uptr access_size) {
|
2013-02-05 22:32:03 +08:00
|
|
|
sptr offset;
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
2014-01-23 19:51:03 +08:00
|
|
|
InternalScopedString str(4096);
|
|
|
|
str.append("%s", d.Location());
|
2013-02-05 22:32:03 +08:00
|
|
|
if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located %zd bytes to the left of", (void *)addr, offset);
|
2012-09-18 15:38:10 +08:00
|
|
|
} else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
|
2013-02-05 22:32:03 +08:00
|
|
|
if (offset < 0) {
|
|
|
|
addr -= offset;
|
|
|
|
offset = 0;
|
|
|
|
}
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located %zd bytes to the right of", (void *)addr, offset);
|
2013-02-05 22:32:03 +08:00
|
|
|
} else if (chunk.AddrIsInside(addr, access_size, &offset)) {
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located %zd bytes inside of", (void*)addr, offset);
|
2012-09-18 15:38:10 +08:00
|
|
|
} else {
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append("%p is located somewhere around (this is AddressSanitizer bug!)",
|
|
|
|
(void *)addr);
|
2012-09-18 15:38:10 +08:00
|
|
|
}
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
|
|
|
|
(void *)(chunk.Beg()), (void *)(chunk.End()));
|
|
|
|
str.append("%s", d.EndLocation());
|
|
|
|
Printf("%s", str.data());
|
2012-09-18 15:38:10 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void DescribeHeapAddress(uptr addr, uptr access_size) {
|
|
|
|
AsanChunkView chunk = FindHeapChunkByAddress(addr);
|
2013-10-14 19:13:54 +08:00
|
|
|
if (!chunk.IsValid()) {
|
|
|
|
Printf("AddressSanitizer can not describe address in more detail "
|
|
|
|
"(wild memory access suspected).\n");
|
|
|
|
return;
|
|
|
|
}
|
2012-09-18 15:38:10 +08:00
|
|
|
DescribeAccessToHeapChunk(chunk, addr, access_size);
|
2016-08-03 23:47:40 +08:00
|
|
|
CHECK_NE(chunk.AllocTid(), kInvalidTid);
|
2013-03-21 19:23:41 +08:00
|
|
|
asanThreadRegistry().CheckLocked();
|
|
|
|
AsanThreadContext *alloc_thread =
|
|
|
|
GetThreadContextByTidLocked(chunk.AllocTid());
|
2014-10-26 11:35:14 +08:00
|
|
|
StackTrace alloc_stack = chunk.GetAllocStack();
|
2012-12-07 23:15:01 +08:00
|
|
|
char tname[128];
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
2015-10-01 08:22:21 +08:00
|
|
|
AsanThreadContext *free_thread = nullptr;
|
2012-09-18 15:38:10 +08:00
|
|
|
if (chunk.FreeTid() != kInvalidTid) {
|
2013-09-10 16:36:21 +08:00
|
|
|
free_thread = GetThreadContextByTidLocked(chunk.FreeTid());
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
|
2013-03-21 19:23:41 +08:00
|
|
|
free_thread->tid,
|
2012-12-18 15:32:16 +08:00
|
|
|
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
|
|
|
|
d.EndAllocation());
|
2014-10-26 11:35:14 +08:00
|
|
|
StackTrace free_stack = chunk.GetFreeStack();
|
2013-12-19 19:25:05 +08:00
|
|
|
free_stack.Print();
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%spreviously allocated by thread T%d%s here:%s\n",
|
2013-03-21 19:23:41 +08:00
|
|
|
d.Allocation(), alloc_thread->tid,
|
2012-12-18 15:32:16 +08:00
|
|
|
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
|
|
|
d.EndAllocation());
|
2012-09-18 15:38:10 +08:00
|
|
|
} else {
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
|
2013-03-21 19:23:41 +08:00
|
|
|
alloc_thread->tid,
|
2012-12-18 15:32:16 +08:00
|
|
|
ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
|
|
|
|
d.EndAllocation());
|
2012-09-18 15:38:10 +08:00
|
|
|
}
|
2013-12-19 19:25:05 +08:00
|
|
|
alloc_stack.Print();
|
2013-09-10 16:36:21 +08:00
|
|
|
DescribeThread(GetCurrentThread());
|
|
|
|
if (free_thread)
|
|
|
|
DescribeThread(free_thread);
|
|
|
|
DescribeThread(alloc_thread);
|
2012-09-18 15:38:10 +08:00
|
|
|
}
|
|
|
|
|
2015-04-23 04:30:19 +08:00
|
|
|
static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) {
|
2012-08-09 17:06:52 +08:00
|
|
|
// Check if this is shadow or shadow gap.
|
|
|
|
if (DescribeAddressIfShadow(addr))
|
|
|
|
return;
|
|
|
|
CHECK(AddrIsInMem(addr));
|
2015-04-23 04:30:19 +08:00
|
|
|
if (DescribeAddressIfGlobal(addr, access_size, bug_type))
|
2012-08-09 17:06:52 +08:00
|
|
|
return;
|
|
|
|
if (DescribeAddressIfStack(addr, access_size))
|
|
|
|
return;
|
|
|
|
// Assume it is a heap address.
|
|
|
|
DescribeHeapAddress(addr, access_size);
|
|
|
|
}
|
|
|
|
|
2012-09-05 15:37:15 +08:00
|
|
|
// ------------------- Thread description -------------------- {{{1
|
|
|
|
|
2013-03-21 19:23:41 +08:00
|
|
|
void DescribeThread(AsanThreadContext *context) {
|
|
|
|
CHECK(context);
|
|
|
|
asanThreadRegistry().CheckLocked();
|
2012-09-05 15:37:15 +08:00
|
|
|
// No need to announce the main thread.
|
2013-03-21 19:23:41 +08:00
|
|
|
if (context->tid == 0 || context->announced) {
|
2012-09-05 15:37:15 +08:00
|
|
|
return;
|
|
|
|
}
|
2013-03-21 19:23:41 +08:00
|
|
|
context->announced = true;
|
2012-12-07 23:15:01 +08:00
|
|
|
char tname[128];
|
2014-01-23 19:51:03 +08:00
|
|
|
InternalScopedString str(1024);
|
|
|
|
str.append("Thread T%d%s", context->tid,
|
|
|
|
ThreadNameWithParenthesis(context->tid, tname, sizeof(tname)));
|
2015-04-06 18:32:45 +08:00
|
|
|
if (context->parent_tid == kInvalidTid) {
|
|
|
|
str.append(" created by unknown thread\n");
|
|
|
|
Printf("%s", str.data());
|
|
|
|
return;
|
|
|
|
}
|
2014-01-23 19:51:03 +08:00
|
|
|
str.append(
|
|
|
|
" created by T%d%s here:\n", context->parent_tid,
|
|
|
|
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
|
|
|
|
Printf("%s", str.data());
|
2014-10-26 11:35:14 +08:00
|
|
|
StackDepotGet(context->stack_id).Print();
|
2012-09-05 15:37:15 +08:00
|
|
|
// Recursively described parent thread if needed.
|
|
|
|
if (flags()->print_full_thread_history) {
|
2013-03-21 19:23:41 +08:00
|
|
|
AsanThreadContext *parent_context =
|
|
|
|
GetThreadContextByTidLocked(context->parent_tid);
|
|
|
|
DescribeThread(parent_context);
|
2012-09-05 15:37:15 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-08-09 17:06:52 +08:00
|
|
|
// -------------------- Different kinds of reports ----------------- {{{1
|
|
|
|
|
2012-08-10 23:13:05 +08:00
|
|
|
// Use ScopedInErrorReport to run common actions just before and
|
|
|
|
// immediately after printing error report.
|
|
|
|
class ScopedInErrorReport {
|
|
|
|
public:
|
2015-11-11 19:59:38 +08:00
|
|
|
explicit ScopedInErrorReport(ReportData *report = nullptr,
|
|
|
|
bool fatal = false) {
|
|
|
|
halt_on_error_ = fatal || flags()->halt_on_error;
|
|
|
|
|
|
|
|
if (lock_.TryLock()) {
|
|
|
|
StartReporting(report);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ASan found two bugs in different threads simultaneously.
|
|
|
|
|
|
|
|
u32 current_tid = GetCurrentTidOrInvalid();
|
2015-11-13 17:46:12 +08:00
|
|
|
if (reporting_thread_tid_ == current_tid ||
|
|
|
|
reporting_thread_tid_ == kInvalidTid) {
|
2015-11-11 19:59:38 +08:00
|
|
|
// This is either asynch signal or nested error during error reporting.
|
2015-11-13 17:46:12 +08:00
|
|
|
// Fail simple to avoid deadlocks in Report().
|
2015-11-11 19:59:38 +08:00
|
|
|
|
|
|
|
// Can't use Report() here because of potential deadlocks
|
|
|
|
// in nested signal handlers.
|
|
|
|
const char msg[] = "AddressSanitizer: nested bug in the same thread, "
|
|
|
|
"aborting.\n";
|
|
|
|
WriteToFile(kStderrFd, msg, sizeof(msg));
|
|
|
|
|
|
|
|
internal__exit(common_flags()->exitcode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (halt_on_error_) {
|
2012-08-10 23:13:05 +08:00
|
|
|
// Do not print more than one report, otherwise they will mix up.
|
|
|
|
// Error reporting functions shouldn't return at this situation, as
|
2015-11-11 19:59:38 +08:00
|
|
|
// they are effectively no-returns.
|
|
|
|
|
2014-08-27 21:43:18 +08:00
|
|
|
Report("AddressSanitizer: while reporting a bug found another one. "
|
2015-11-11 19:59:38 +08:00
|
|
|
"Ignoring.\n");
|
|
|
|
|
|
|
|
// Sleep long enough to make sure that the thread which started
|
|
|
|
// to print an error report will finish doing it.
|
|
|
|
SleepForSeconds(Max(100, flags()->sleep_before_dying + 1));
|
|
|
|
|
2013-02-20 21:54:32 +08:00
|
|
|
// If we're still not dead for some reason, use raw _exit() instead of
|
2012-11-19 19:22:22 +08:00
|
|
|
// Die() to bypass any additional checks.
|
2015-08-22 04:49:37 +08:00
|
|
|
internal__exit(common_flags()->exitcode);
|
2015-11-11 19:59:38 +08:00
|
|
|
} else {
|
|
|
|
// The other thread will eventually finish reporting
|
|
|
|
// so it's safe to wait
|
|
|
|
lock_.Lock();
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
2015-11-11 19:59:38 +08:00
|
|
|
|
|
|
|
StartReporting(report);
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
2015-11-11 19:59:38 +08:00
|
|
|
|
|
|
|
~ScopedInErrorReport() {
|
2012-08-10 23:13:05 +08:00
|
|
|
// Make sure the current thread is announced.
|
2013-09-10 16:36:21 +08:00
|
|
|
DescribeThread(GetCurrentThread());
|
2014-05-12 16:01:51 +08:00
|
|
|
// We may want to grab this lock again when printing stats.
|
|
|
|
asanThreadRegistry().Unlock();
|
2012-08-10 23:13:05 +08:00
|
|
|
// Print memory stats.
|
2013-01-28 15:34:22 +08:00
|
|
|
if (flags()->print_stats)
|
|
|
|
__asan_print_accumulated_stats();
|
2015-11-21 02:42:01 +08:00
|
|
|
|
2016-01-18 15:55:12 +08:00
|
|
|
if (common_flags()->print_cmdline)
|
|
|
|
PrintCmdline();
|
|
|
|
|
2015-11-21 02:42:01 +08:00
|
|
|
// Copy the message buffer so that we could start logging without holding a
|
|
|
|
// lock that gets aquired during printing.
|
|
|
|
InternalScopedBuffer<char> buffer_copy(kErrorMessageBufferSize);
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
{
|
|
|
|
BlockingMutexLock l(&error_message_buf_mutex);
|
2015-11-21 02:42:01 +08:00
|
|
|
internal_memcpy(buffer_copy.data(),
|
|
|
|
error_message_buffer, kErrorMessageBufferSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
LogFullErrorReport(buffer_copy.data());
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
|
2015-11-21 02:42:01 +08:00
|
|
|
if (error_report_callback) {
|
|
|
|
error_report_callback(buffer_copy.data());
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
2015-11-11 19:59:38 +08:00
|
|
|
CommonSanitizerReportMutex.Unlock();
|
|
|
|
reporting_thread_tid_ = kInvalidTid;
|
|
|
|
lock_.Unlock();
|
|
|
|
if (halt_on_error_) {
|
|
|
|
Report("ABORTING\n");
|
|
|
|
Die();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
void StartReporting(ReportData *report) {
|
|
|
|
if (report) report_data = *report;
|
|
|
|
report_happened = true;
|
|
|
|
ASAN_ON_ERROR();
|
|
|
|
// Make sure the registry and sanitizer report mutexes are locked while
|
|
|
|
// we're printing an error report.
|
|
|
|
// We can lock them only here to avoid self-deadlock in case of
|
|
|
|
// recursive reports.
|
|
|
|
asanThreadRegistry().Lock();
|
|
|
|
CommonSanitizerReportMutex.Lock();
|
|
|
|
reporting_thread_tid_ = GetCurrentTidOrInvalid();
|
|
|
|
Printf("===================================================="
|
|
|
|
"=============\n");
|
2012-08-10 23:13:05 +08:00
|
|
|
}
|
2015-11-11 19:59:38 +08:00
|
|
|
|
|
|
|
static StaticSpinMutex lock_;
|
|
|
|
static u32 reporting_thread_tid_;
|
|
|
|
bool halt_on_error_;
|
2012-08-10 23:13:05 +08:00
|
|
|
};
|
|
|
|
|
2015-11-11 19:59:38 +08:00
|
|
|
StaticSpinMutex ScopedInErrorReport::lock_;
|
2016-05-31 16:47:18 +08:00
|
|
|
u32 ScopedInErrorReport::reporting_thread_tid_ = kInvalidTid;
|
2015-11-11 19:59:38 +08:00
|
|
|
|
2014-11-25 21:00:21 +08:00
|
|
|
void ReportStackOverflow(const SignalContext &sig) {
|
2016-05-06 15:09:22 +08:00
|
|
|
ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true);
|
2014-02-19 21:40:41 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
|
|
|
Report(
|
|
|
|
"ERROR: AddressSanitizer: stack-overflow on address %p"
|
2014-07-11 20:14:46 +08:00
|
|
|
" (pc %p bp %p sp %p T%d)\n",
|
2014-11-25 21:00:21 +08:00
|
|
|
(void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp,
|
2014-02-19 21:40:41 +08:00
|
|
|
GetCurrentTidOrInvalid());
|
|
|
|
Printf("%s", d.EndWarning());
|
2016-02-10 07:46:43 +08:00
|
|
|
ScarinessScore::PrintSimple(10, "stack-overflow");
|
2014-11-25 21:00:21 +08:00
|
|
|
GET_STACK_TRACE_SIGNAL(sig);
|
2014-02-19 21:40:41 +08:00
|
|
|
stack.Print();
|
|
|
|
ReportErrorSummary("stack-overflow", &stack);
|
2014-02-18 19:49:52 +08:00
|
|
|
}
|
|
|
|
|
2015-08-07 01:52:54 +08:00
|
|
|
void ReportDeadlySignal(const char *description, const SignalContext &sig) {
|
2016-02-09 06:50:25 +08:00
|
|
|
ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true);
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
2014-02-18 19:49:52 +08:00
|
|
|
Report(
|
2014-07-11 19:57:41 +08:00
|
|
|
"ERROR: AddressSanitizer: %s on unknown address %p"
|
2014-07-11 20:14:46 +08:00
|
|
|
" (pc %p bp %p sp %p T%d)\n",
|
2014-11-25 21:00:21 +08:00
|
|
|
description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp,
|
|
|
|
(void *)sig.sp, GetCurrentTidOrInvalid());
|
2016-02-04 10:02:09 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore SS;
|
2016-02-04 10:02:09 +08:00
|
|
|
if (sig.pc < GetPageSizeCached())
|
2014-09-19 20:37:00 +08:00
|
|
|
Report("Hint: pc points to the zero page.\n");
|
2016-02-04 10:02:09 +08:00
|
|
|
if (sig.is_memory_access) {
|
2016-02-09 06:50:25 +08:00
|
|
|
const char *access_type =
|
|
|
|
sig.write_flag == SignalContext::WRITE
|
|
|
|
? "WRITE"
|
|
|
|
: (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
|
|
|
|
Report("The signal is caused by a %s memory access.\n", access_type);
|
2016-02-09 03:21:08 +08:00
|
|
|
if (sig.addr < GetPageSizeCached()) {
|
2016-02-04 10:02:09 +08:00
|
|
|
Report("Hint: address points to the zero page.\n");
|
2016-02-09 03:21:08 +08:00
|
|
|
SS.Scare(10, "null-deref");
|
|
|
|
} else if (sig.addr == sig.pc) {
|
|
|
|
SS.Scare(60, "wild-jump");
|
2016-02-09 06:50:25 +08:00
|
|
|
} else if (sig.write_flag == SignalContext::WRITE) {
|
2016-02-09 03:21:08 +08:00
|
|
|
SS.Scare(30, "wild-addr-write");
|
2016-02-09 06:50:25 +08:00
|
|
|
} else if (sig.write_flag == SignalContext::READ) {
|
2016-02-09 03:21:08 +08:00
|
|
|
SS.Scare(20, "wild-addr-read");
|
2016-02-09 06:50:25 +08:00
|
|
|
} else {
|
|
|
|
SS.Scare(25, "wild-addr");
|
2016-02-09 03:21:08 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
SS.Scare(10, "signal");
|
2014-09-19 20:37:00 +08:00
|
|
|
}
|
2016-02-09 03:21:08 +08:00
|
|
|
SS.Print();
|
2014-11-25 21:00:21 +08:00
|
|
|
GET_STACK_TRACE_SIGNAL(sig);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack.Print();
|
2014-11-25 21:00:21 +08:00
|
|
|
MaybeDumpInstructionBytes(sig.pc);
|
2013-10-14 19:13:54 +08:00
|
|
|
Printf("AddressSanitizer can not provide additional info.\n");
|
2015-08-07 01:52:54 +08:00
|
|
|
ReportErrorSummary(description, &stack);
|
2012-08-09 15:40:58 +08:00
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
|
2012-08-10 23:13:05 +08:00
|
|
|
ScopedInErrorReport in_report;
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
2013-03-26 16:01:37 +08:00
|
|
|
char tname[128];
|
|
|
|
u32 curr_tid = GetCurrentTidOrInvalid();
|
|
|
|
Report("ERROR: AddressSanitizer: attempting double-free on %p in "
|
|
|
|
"thread T%d%s:\n",
|
|
|
|
addr, curr_tid,
|
|
|
|
ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2013-11-13 22:46:58 +08:00
|
|
|
CHECK_GT(free_stack->size, 0);
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore::PrintSimple(42, "double-free");
|
2013-11-13 22:46:58 +08:00
|
|
|
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack.Print();
|
2012-08-09 16:15:46 +08:00
|
|
|
DescribeHeapAddress(addr, 1);
|
2013-11-13 22:46:58 +08:00
|
|
|
ReportErrorSummary("double-free", &stack);
|
2012-08-09 16:15:46 +08:00
|
|
|
}
|
|
|
|
|
2016-04-14 05:04:27 +08:00
|
|
|
void ReportNewDeleteSizeMismatch(uptr addr, uptr alloc_size, uptr delete_size,
|
2014-10-26 11:35:14 +08:00
|
|
|
BufferedStackTrace *free_stack) {
|
2014-07-30 17:48:23 +08:00
|
|
|
ScopedInErrorReport in_report;
|
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
|
|
|
char tname[128];
|
|
|
|
u32 curr_tid = GetCurrentTidOrInvalid();
|
2014-07-30 19:20:37 +08:00
|
|
|
Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in "
|
2014-07-30 17:48:23 +08:00
|
|
|
"thread T%d%s:\n",
|
|
|
|
addr, curr_tid,
|
|
|
|
ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
|
2014-07-30 19:20:37 +08:00
|
|
|
Printf("%s object passed to delete has wrong type:\n", d.EndWarning());
|
|
|
|
Printf(" size of the allocated type: %zd bytes;\n"
|
|
|
|
" size of the deallocated type: %zd bytes.\n",
|
2016-04-14 05:04:27 +08:00
|
|
|
alloc_size, delete_size);
|
2014-07-30 17:48:23 +08:00
|
|
|
CHECK_GT(free_stack->size, 0);
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore::PrintSimple(10, "new-delete-type-mismatch");
|
2014-07-30 17:48:23 +08:00
|
|
|
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
|
|
|
stack.Print();
|
|
|
|
DescribeHeapAddress(addr, 1);
|
2014-07-30 19:20:37 +08:00
|
|
|
ReportErrorSummary("new-delete-type-mismatch", &stack);
|
2015-11-18 08:24:32 +08:00
|
|
|
Report("HINT: if you don't care about these errors you may set "
|
2014-07-30 19:20:37 +08:00
|
|
|
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
|
2014-07-30 17:48:23 +08:00
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
|
2012-08-10 23:13:05 +08:00
|
|
|
ScopedInErrorReport in_report;
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
2013-03-26 16:01:37 +08:00
|
|
|
char tname[128];
|
|
|
|
u32 curr_tid = GetCurrentTidOrInvalid();
|
2012-10-15 21:04:58 +08:00
|
|
|
Report("ERROR: AddressSanitizer: attempting free on address "
|
2013-03-26 16:01:37 +08:00
|
|
|
"which was not malloc()-ed: %p in thread T%d%s\n", addr,
|
|
|
|
curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2013-11-13 22:46:58 +08:00
|
|
|
CHECK_GT(free_stack->size, 0);
|
2016-02-10 07:46:43 +08:00
|
|
|
ScarinessScore::PrintSimple(40, "bad-free");
|
2013-11-13 22:46:58 +08:00
|
|
|
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack.Print();
|
2012-08-10 23:13:05 +08:00
|
|
|
DescribeHeapAddress(addr, 1);
|
2013-11-13 22:46:58 +08:00
|
|
|
ReportErrorSummary("bad-free", &stack);
|
2012-08-09 16:15:46 +08:00
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
|
2012-12-21 16:53:59 +08:00
|
|
|
AllocType alloc_type,
|
|
|
|
AllocType dealloc_type) {
|
|
|
|
static const char *alloc_names[] =
|
|
|
|
{"INVALID", "malloc", "operator new", "operator new []"};
|
|
|
|
static const char *dealloc_names[] =
|
|
|
|
{"INVALID", "free", "operator delete", "operator delete []"};
|
|
|
|
CHECK_NE(alloc_type, dealloc_type);
|
|
|
|
ScopedInErrorReport in_report;
|
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
|
|
|
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());
|
2013-11-13 22:46:58 +08:00
|
|
|
CHECK_GT(free_stack->size, 0);
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore::PrintSimple(10, "alloc-dealloc-mismatch");
|
2013-11-13 22:46:58 +08:00
|
|
|
GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack.Print();
|
2012-12-21 16:53:59 +08:00
|
|
|
DescribeHeapAddress(addr, 1);
|
2013-11-13 22:46:58 +08:00
|
|
|
ReportErrorSummary("alloc-dealloc-mismatch", &stack);
|
2015-11-18 08:24:32 +08:00
|
|
|
Report("HINT: if you don't care about these errors you may set "
|
2012-12-21 16:53:59 +08:00
|
|
|
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
|
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
|
2012-08-10 23:13:05 +08:00
|
|
|
ScopedInErrorReport in_report;
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
2012-10-15 21:04:58 +08:00
|
|
|
Report("ERROR: AddressSanitizer: attempting to call "
|
2012-08-09 16:15:46 +08:00
|
|
|
"malloc_usable_size() for pointer which is "
|
|
|
|
"not owned: %p\n", addr);
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2013-12-19 19:25:05 +08:00
|
|
|
stack->Print();
|
2012-08-09 16:15:46 +08:00
|
|
|
DescribeHeapAddress(addr, 1);
|
2013-11-02 01:02:14 +08:00
|
|
|
ReportErrorSummary("bad-malloc_usable_size", stack);
|
2012-08-09 16:15:46 +08:00
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
|
|
|
|
BufferedStackTrace *stack) {
|
2012-08-10 23:13:05 +08:00
|
|
|
ScopedInErrorReport in_report;
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
2012-10-15 21:04:58 +08:00
|
|
|
Report("ERROR: AddressSanitizer: attempting to call "
|
2014-07-08 01:39:31 +08:00
|
|
|
"__sanitizer_get_allocated_size() for pointer which is "
|
2012-08-09 16:15:46 +08:00
|
|
|
"not owned: %p\n", addr);
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2013-12-19 19:25:05 +08:00
|
|
|
stack->Print();
|
2012-08-09 16:15:46 +08:00
|
|
|
DescribeHeapAddress(addr, 1);
|
2014-07-08 01:39:31 +08:00
|
|
|
ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
|
2012-08-09 16:15:46 +08:00
|
|
|
}
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportStringFunctionMemoryRangesOverlap(const char *function,
|
|
|
|
const char *offset1, uptr length1,
|
|
|
|
const char *offset2, uptr length2,
|
|
|
|
BufferedStackTrace *stack) {
|
2012-08-10 23:13:05 +08:00
|
|
|
ScopedInErrorReport in_report;
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
2013-02-06 20:36:49 +08:00
|
|
|
char bug_type[100];
|
|
|
|
internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.Warning());
|
2013-02-06 20:36:49 +08:00
|
|
|
Report("ERROR: AddressSanitizer: %s: "
|
2012-08-09 16:32:33 +08:00
|
|
|
"memory ranges [%p,%p) and [%p, %p) overlap\n", \
|
2013-02-06 20:36:49 +08:00
|
|
|
bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore::PrintSimple(10, bug_type);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack->Print();
|
2015-04-23 04:30:19 +08:00
|
|
|
DescribeAddress((uptr)offset1, length1, bug_type);
|
|
|
|
DescribeAddress((uptr)offset2, length2, bug_type);
|
2013-11-02 01:02:14 +08:00
|
|
|
ReportErrorSummary(bug_type, stack);
|
2012-08-09 16:32:33 +08:00
|
|
|
}
|
2012-08-09 16:15:46 +08:00
|
|
|
|
2014-04-14 17:50:52 +08:00
|
|
|
void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
|
2014-10-26 11:35:14 +08:00
|
|
|
BufferedStackTrace *stack) {
|
2014-04-14 17:50:52 +08:00
|
|
|
ScopedInErrorReport in_report;
|
|
|
|
Decorator d;
|
|
|
|
const char *bug_type = "negative-size-param";
|
|
|
|
Printf("%s", d.Warning());
|
|
|
|
Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
|
|
|
|
Printf("%s", d.EndWarning());
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore::PrintSimple(10, bug_type);
|
2014-04-14 17:50:52 +08:00
|
|
|
stack->Print();
|
2015-04-23 04:30:19 +08:00
|
|
|
DescribeAddress(offset, size, bug_type);
|
2014-04-14 17:50:52 +08:00
|
|
|
ReportErrorSummary(bug_type, stack);
|
|
|
|
}
|
|
|
|
|
2013-12-23 15:01:43 +08:00
|
|
|
void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
|
|
|
|
uptr old_mid, uptr new_mid,
|
2014-10-26 11:35:14 +08:00
|
|
|
BufferedStackTrace *stack) {
|
2013-12-23 15:01:43 +08:00
|
|
|
ScopedInErrorReport in_report;
|
|
|
|
Report("ERROR: AddressSanitizer: bad parameters to "
|
|
|
|
"__sanitizer_annotate_contiguous_container:\n"
|
|
|
|
" beg : %p\n"
|
|
|
|
" end : %p\n"
|
|
|
|
" old_mid : %p\n"
|
|
|
|
" new_mid : %p\n",
|
|
|
|
beg, end, old_mid, new_mid);
|
2015-02-26 01:03:34 +08:00
|
|
|
uptr granularity = SHADOW_GRANULARITY;
|
|
|
|
if (!IsAligned(beg, granularity))
|
|
|
|
Report("ERROR: beg is not aligned by %d\n", granularity);
|
2013-12-23 15:01:43 +08:00
|
|
|
stack->Print();
|
|
|
|
ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
|
|
|
|
}
|
|
|
|
|
2014-06-20 16:24:12 +08:00
|
|
|
void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
|
|
|
|
const __asan_global *g2, u32 stack_id2) {
|
2014-04-25 16:58:28 +08:00
|
|
|
ScopedInErrorReport in_report;
|
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
|
|
|
Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
|
|
|
|
Printf("%s", d.EndWarning());
|
2014-07-03 00:54:41 +08:00
|
|
|
InternalScopedString g1_loc(256), g2_loc(256);
|
|
|
|
PrintGlobalLocation(&g1_loc, *g1);
|
|
|
|
PrintGlobalLocation(&g2_loc, *g2);
|
2014-07-12 07:34:26 +08:00
|
|
|
Printf(" [1] size=%zd '%s' %s\n", g1->size,
|
|
|
|
MaybeDemangleGlobalName(g1->name), g1_loc.data());
|
|
|
|
Printf(" [2] size=%zd '%s' %s\n", g2->size,
|
|
|
|
MaybeDemangleGlobalName(g2->name), g2_loc.data());
|
2014-06-20 16:24:12 +08:00
|
|
|
if (stack_id1 && stack_id2) {
|
|
|
|
Printf("These globals were registered at these points:\n");
|
|
|
|
Printf(" [1]:\n");
|
2014-10-26 11:35:14 +08:00
|
|
|
StackDepotGet(stack_id1).Print();
|
2014-06-20 16:24:12 +08:00
|
|
|
Printf(" [2]:\n");
|
2014-10-26 11:35:14 +08:00
|
|
|
StackDepotGet(stack_id2).Print();
|
2014-06-20 16:24:12 +08:00
|
|
|
}
|
2015-11-18 08:24:32 +08:00
|
|
|
Report("HINT: if you don't care about these errors you may set "
|
2014-04-25 16:58:28 +08:00
|
|
|
"ASAN_OPTIONS=detect_odr_violation=0\n");
|
2014-07-12 07:34:26 +08:00
|
|
|
InternalScopedString error_msg(256);
|
|
|
|
error_msg.append("odr-violation: global '%s' at %s",
|
|
|
|
MaybeDemangleGlobalName(g1->name), g1_loc.data());
|
|
|
|
ReportErrorSummary(error_msg.data());
|
2014-04-25 16:58:28 +08:00
|
|
|
}
|
|
|
|
|
2014-02-27 20:45:36 +08:00
|
|
|
// ----------------------- CheckForInvalidPointerPair ----------- {{{1
|
|
|
|
static NOINLINE void
|
|
|
|
ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) {
|
|
|
|
ScopedInErrorReport in_report;
|
2015-04-23 04:30:19 +08:00
|
|
|
const char *bug_type = "invalid-pointer-pair";
|
2014-02-27 20:45:36 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
|
|
|
Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2);
|
|
|
|
Printf("%s", d.EndWarning());
|
|
|
|
GET_STACK_TRACE_FATAL(pc, bp);
|
|
|
|
stack.Print();
|
2015-04-23 04:30:19 +08:00
|
|
|
DescribeAddress(a1, 1, bug_type);
|
|
|
|
DescribeAddress(a2, 1, bug_type);
|
|
|
|
ReportErrorSummary(bug_type, &stack);
|
2014-02-27 20:45:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
|
|
|
|
if (!flags()->detect_invalid_pointer_pairs) return;
|
|
|
|
uptr a1 = reinterpret_cast<uptr>(p1);
|
|
|
|
uptr a2 = reinterpret_cast<uptr>(p2);
|
|
|
|
AsanChunkView chunk1 = FindHeapChunkByAddress(a1);
|
|
|
|
AsanChunkView chunk2 = FindHeapChunkByAddress(a2);
|
[asan] Assert in __sanitizer_ptr_{sub,cmp} if one of the pointers was freed.
Summary:
This (partially) implements the check mentioned at
http://kristerw.blogspot.co.uk/2016/04/dangling-pointers-and-undefined-behavior.html
(via John Regehr)
Quoting:
"That the behavior is undefined follows from C11 6.2.4 "Storage
durations of objects"
The lifetime of an object is the portion of program execution during
which storage is guaranteed to be reserved for it. An object exists, has
a constant address, and retains its last-stored value throughout its
lifetime. If an object is referred to outside of its lifetime, the
behavior is undefined. The value of a pointer becomes indeterminate when
the object it points to (or just past) reaches the end of its lifetime.
and 7.22.3 "Memory management functions" that says that free ends the
lifetime of objects
The lifetime of an allocated object extends from the allocation until
the deallocation.
"
We can probably implement this for stack variables too, but I think this
is a good start to see if there's interest in this check.
We can also hide this behind a flag, too.
Reviewers: samsonov, kcc, rsmith, regehr
Subscribers: kubabrecka, llvm-commits
Differential Revision: http://reviews.llvm.org/D19691
llvm-svn: 268097
2016-04-30 04:37:34 +08:00
|
|
|
bool valid1 = chunk1.IsAllocated();
|
|
|
|
bool valid2 = chunk2.IsAllocated();
|
|
|
|
if (!valid1 || !valid2 || !chunk1.Eq(chunk2)) {
|
|
|
|
GET_CALLER_PC_BP_SP;
|
2014-02-27 20:45:36 +08:00
|
|
|
return ReportInvalidPointerPair(pc, bp, sp, a1, a2);
|
|
|
|
}
|
|
|
|
}
|
2012-08-09 20:15:40 +08:00
|
|
|
// ----------------------- Mac-specific reports ----------------- {{{1
|
|
|
|
|
2014-10-26 11:35:14 +08:00
|
|
|
void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
|
|
|
|
BufferedStackTrace *stack) {
|
2012-08-10 23:13:05 +08:00
|
|
|
ScopedInErrorReport in_report;
|
2012-08-28 19:34:40 +08:00
|
|
|
Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
|
2012-08-09 20:15:40 +08:00
|
|
|
"This is an unrecoverable problem, exiting now.\n",
|
|
|
|
addr);
|
|
|
|
PrintZoneForPointer(addr, zone_ptr, zone_name);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack->Print();
|
2012-08-10 23:13:05 +08:00
|
|
|
DescribeHeapAddress(addr, 1);
|
2012-08-09 20:15:40 +08:00
|
|
|
}
|
|
|
|
|
2015-12-10 16:08:53 +08:00
|
|
|
// -------------- SuppressErrorReport -------------- {{{1
|
|
|
|
// Avoid error reports duplicating for ASan recover mode.
|
|
|
|
static bool SuppressErrorReport(uptr pc) {
|
|
|
|
if (!common_flags()->suppress_equal_pcs) return false;
|
|
|
|
for (unsigned i = 0; i < kAsanBuggyPcPoolSize; i++) {
|
2015-12-10 19:07:19 +08:00
|
|
|
uptr cmp = atomic_load_relaxed(&AsanBuggyPcPool[i]);
|
2015-12-10 16:08:53 +08:00
|
|
|
if (cmp == 0 && atomic_compare_exchange_strong(&AsanBuggyPcPool[i], &cmp,
|
|
|
|
pc, memory_order_relaxed))
|
|
|
|
return false;
|
|
|
|
if (cmp == pc) return true;
|
|
|
|
}
|
|
|
|
Die();
|
|
|
|
}
|
|
|
|
|
2016-01-21 03:49:12 +08:00
|
|
|
static void PrintContainerOverflowHint() {
|
|
|
|
Printf("HINT: if you don't care about these errors you may set "
|
|
|
|
"ASAN_OPTIONS=detect_container_overflow=0.\n"
|
|
|
|
"If you suspect a false positive see also: "
|
|
|
|
"https://github.com/google/sanitizers/wiki/"
|
|
|
|
"AddressSanitizerContainerOverflow.\n");
|
|
|
|
}
|
|
|
|
|
2016-02-10 07:46:43 +08:00
|
|
|
static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
|
|
|
|
return s[-1] > 127 && s[1] > 127;
|
|
|
|
}
|
|
|
|
|
2015-11-11 20:23:59 +08:00
|
|
|
void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
|
|
|
|
uptr access_size, u32 exp, bool fatal) {
|
2015-12-10 16:08:53 +08:00
|
|
|
if (!fatal && SuppressErrorReport(pc)) return;
|
2015-01-23 07:36:47 +08:00
|
|
|
ENABLE_FRAME_POINTER;
|
2016-02-09 03:21:08 +08:00
|
|
|
ScarinessScore SS;
|
|
|
|
|
|
|
|
if (access_size) {
|
|
|
|
if (access_size <= 9) {
|
|
|
|
char desr[] = "?-byte";
|
|
|
|
desr[0] = '0' + access_size;
|
|
|
|
SS.Scare(access_size + access_size / 2, desr);
|
|
|
|
} else if (access_size >= 10) {
|
|
|
|
SS.Scare(15, "multi-byte");
|
|
|
|
}
|
|
|
|
is_write ? SS.Scare(20, "write") : SS.Scare(1, "read");
|
|
|
|
}
|
2015-01-23 08:14:22 +08:00
|
|
|
|
2015-03-18 00:59:11 +08:00
|
|
|
// Optimization experiments.
|
|
|
|
// The experiments can be used to evaluate potential optimizations that remove
|
|
|
|
// instrumentation (assess false negatives). Instead of completely removing
|
|
|
|
// some instrumentation, compiler can emit special calls into runtime
|
|
|
|
// (e.g. __asan_report_exp_load1 instead of __asan_report_load1) and pass
|
|
|
|
// mask of experiments (exp).
|
|
|
|
// The reaction to a non-zero value of exp is to be defined.
|
|
|
|
(void)exp;
|
|
|
|
|
2012-08-10 23:13:05 +08:00
|
|
|
// Determine the error type.
|
2012-08-09 18:56:57 +08:00
|
|
|
const char *bug_descr = "unknown-crash";
|
2016-01-21 03:49:12 +08:00
|
|
|
u8 shadow_val = 0;
|
2012-08-09 18:56:57 +08:00
|
|
|
if (AddrIsInMem(addr)) {
|
|
|
|
u8 *shadow_addr = (u8*)MemToShadow(addr);
|
|
|
|
// If we are accessing 16 bytes, look at the second shadow byte.
|
|
|
|
if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
|
|
|
|
shadow_addr++;
|
|
|
|
// If we are in the partial right redzone, look at the next shadow byte.
|
|
|
|
if (*shadow_addr > 0 && *shadow_addr < 128)
|
|
|
|
shadow_addr++;
|
2016-02-09 03:21:08 +08:00
|
|
|
bool far_from_bounds = false;
|
2016-01-21 03:49:12 +08:00
|
|
|
shadow_val = *shadow_addr;
|
2016-02-09 03:21:08 +08:00
|
|
|
int bug_type_score = 0;
|
2016-03-26 08:00:19 +08:00
|
|
|
// For use-after-frees reads are almost as bad as writes.
|
|
|
|
int read_after_free_bonus = 0;
|
2016-01-21 03:49:12 +08:00
|
|
|
switch (shadow_val) {
|
2012-08-09 18:56:57 +08:00
|
|
|
case kAsanHeapLeftRedzoneMagic:
|
|
|
|
case kAsanHeapRightRedzoneMagic:
|
2014-08-04 20:43:13 +08:00
|
|
|
case kAsanArrayCookieMagic:
|
2012-08-09 18:56:57 +08:00
|
|
|
bug_descr = "heap-buffer-overflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 10;
|
2016-02-10 07:46:43 +08:00
|
|
|
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
|
|
|
case kAsanHeapFreeMagic:
|
|
|
|
bug_descr = "heap-use-after-free";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 20;
|
2016-03-26 08:00:19 +08:00
|
|
|
if (!is_write) read_after_free_bonus = 18;
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
|
|
|
case kAsanStackLeftRedzoneMagic:
|
|
|
|
bug_descr = "stack-buffer-underflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 25;
|
2016-02-10 07:46:43 +08:00
|
|
|
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
2012-08-21 22:10:25 +08:00
|
|
|
case kAsanInitializationOrderMagic:
|
|
|
|
bug_descr = "initialization-order-fiasco";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 1;
|
2012-08-21 22:10:25 +08:00
|
|
|
break;
|
2012-08-09 18:56:57 +08:00
|
|
|
case kAsanStackMidRedzoneMagic:
|
|
|
|
case kAsanStackRightRedzoneMagic:
|
|
|
|
case kAsanStackPartialRedzoneMagic:
|
|
|
|
bug_descr = "stack-buffer-overflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 25;
|
2016-02-10 07:46:43 +08:00
|
|
|
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
|
|
|
case kAsanStackAfterReturnMagic:
|
|
|
|
bug_descr = "stack-use-after-return";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 30;
|
2016-03-26 08:00:19 +08:00
|
|
|
if (!is_write) read_after_free_bonus = 18;
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
|
|
|
case kAsanUserPoisonedMemoryMagic:
|
|
|
|
bug_descr = "use-after-poison";
|
2016-02-10 07:46:43 +08:00
|
|
|
bug_type_score = 20;
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
2013-11-19 16:40:07 +08:00
|
|
|
case kAsanContiguousContainerOOBMagic:
|
2013-11-21 20:23:52 +08:00
|
|
|
bug_descr = "container-overflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 10;
|
2013-11-19 16:40:07 +08:00
|
|
|
break;
|
2012-12-04 09:38:15 +08:00
|
|
|
case kAsanStackUseAfterScopeMagic:
|
|
|
|
bug_descr = "stack-use-after-scope";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 10;
|
2012-12-04 09:38:15 +08:00
|
|
|
break;
|
2012-08-09 18:56:57 +08:00
|
|
|
case kAsanGlobalRedzoneMagic:
|
|
|
|
bug_descr = "global-buffer-overflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 10;
|
2016-02-10 07:46:43 +08:00
|
|
|
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
2012-08-09 18:56:57 +08:00
|
|
|
break;
|
2014-10-17 09:22:37 +08:00
|
|
|
case kAsanIntraObjectRedzone:
|
|
|
|
bug_descr = "intra-object-overflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 10;
|
2014-10-17 09:22:37 +08:00
|
|
|
break;
|
2014-11-21 18:32:05 +08:00
|
|
|
case kAsanAllocaLeftMagic:
|
|
|
|
case kAsanAllocaRightMagic:
|
|
|
|
bug_descr = "dynamic-stack-buffer-overflow";
|
2016-02-09 03:21:08 +08:00
|
|
|
bug_type_score = 25;
|
2016-02-10 07:46:43 +08:00
|
|
|
far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
|
2014-11-21 18:32:05 +08:00
|
|
|
break;
|
2012-08-09 18:56:57 +08:00
|
|
|
}
|
2016-03-26 08:00:19 +08:00
|
|
|
SS.Scare(bug_type_score + read_after_free_bonus, bug_descr);
|
2016-02-09 03:21:08 +08:00
|
|
|
if (far_from_bounds)
|
|
|
|
SS.Scare(10, "far-from-bounds");
|
2012-08-09 18:56:57 +08:00
|
|
|
}
|
2014-09-27 03:15:32 +08:00
|
|
|
|
|
|
|
ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
|
|
|
|
bug_descr };
|
2015-11-11 19:59:38 +08:00
|
|
|
ScopedInErrorReport in_report(&report, fatal);
|
2014-09-27 03:15:32 +08:00
|
|
|
|
2012-12-18 15:32:16 +08:00
|
|
|
Decorator d;
|
|
|
|
Printf("%s", d.Warning());
|
2012-10-15 21:04:58 +08:00
|
|
|
Report("ERROR: AddressSanitizer: %s on address "
|
2014-07-11 20:14:46 +08:00
|
|
|
"%p at pc %p bp %p sp %p\n",
|
2012-08-09 18:56:57 +08:00
|
|
|
bug_descr, (void*)addr, pc, bp, sp);
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s", d.EndWarning());
|
2012-08-09 18:56:57 +08:00
|
|
|
|
2013-03-20 17:23:28 +08:00
|
|
|
u32 curr_tid = GetCurrentTidOrInvalid();
|
2012-12-07 23:15:01 +08:00
|
|
|
char tname[128];
|
2012-12-18 15:32:16 +08:00
|
|
|
Printf("%s%s of size %zu at %p thread T%d%s%s\n",
|
|
|
|
d.Access(),
|
|
|
|
access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
|
|
|
|
access_size, (void*)addr, curr_tid,
|
|
|
|
ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)),
|
|
|
|
d.EndAccess());
|
2012-08-09 18:56:57 +08:00
|
|
|
|
2016-02-09 03:21:08 +08:00
|
|
|
SS.Print();
|
2012-12-13 17:34:23 +08:00
|
|
|
GET_STACK_TRACE_FATAL(pc, bp);
|
2013-12-19 19:25:05 +08:00
|
|
|
stack.Print();
|
2012-08-09 18:56:57 +08:00
|
|
|
|
2015-04-23 04:30:19 +08:00
|
|
|
DescribeAddress(addr, access_size, bug_descr);
|
2016-01-21 03:49:12 +08:00
|
|
|
if (shadow_val == kAsanContiguousContainerOOBMagic)
|
|
|
|
PrintContainerOverflowHint();
|
2013-11-02 01:02:14 +08:00
|
|
|
ReportErrorSummary(bug_descr, &stack);
|
2012-08-10 23:13:05 +08:00
|
|
|
PrintShadowMemoryForAddress(addr);
|
2012-08-09 18:56:57 +08:00
|
|
|
}
|
|
|
|
|
2015-11-11 19:59:38 +08:00
|
|
|
} // namespace __asan
|
|
|
|
|
|
|
|
// --------------------------- Interface --------------------- {{{1
|
|
|
|
using namespace __asan; // NOLINT
|
|
|
|
|
|
|
|
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
|
|
|
|
uptr access_size, u32 exp) {
|
|
|
|
ENABLE_FRAME_POINTER;
|
|
|
|
bool fatal = flags()->halt_on_error;
|
|
|
|
ReportGenericError(pc, bp, sp, addr, is_write, access_size, exp, fatal);
|
|
|
|
}
|
|
|
|
|
2012-08-09 18:56:57 +08:00
|
|
|
void NOINLINE __asan_set_error_report_callback(void (*callback)(const char*)) {
|
Reapply: [asan] On OS X, log reports to syslog and os_trace
When ASan currently detects a bug, by default it will only print out the text
of the report to stderr. This patch changes this behavior and writes the full
text of the report to syslog before we terminate the process. It also calls
os_trace (Activity Tracing available on OS X and iOS) with a message saying
that the report is available in syslog. This is useful, because this message
will be shown in the crash log.
For this to work, the patch makes sure we store the full report into
error_message_buffer unconditionally, and it also strips out ANSI escape
sequences from the report (they are used when producing colored reports).
I've initially tried to log to syslog during printing, which is done on Android
right now. The advantage is that if we crash during error reporting or the
produced error does not go through ScopedInErrorReport, we would still get a
(partial) message in the syslog. However, that solution is very problematic on
OS X. One issue is that the logging routine uses GCD, which may spawn a new
thread on its behalf. In many cases, the reporting logic locks threadRegistry,
which leads to deadlocks.
Reviewed at http://reviews.llvm.org/D13452
(In addition, add sanitizer_common_libcdep.cc to buildgo.sh to avoid
build failures on Linux.)
llvm-svn: 253688
2015-11-21 02:41:44 +08:00
|
|
|
BlockingMutexLock l(&error_message_buf_mutex);
|
2012-08-09 18:56:57 +08:00
|
|
|
error_report_callback = callback;
|
|
|
|
}
|
2012-08-13 19:23:40 +08:00
|
|
|
|
2012-12-29 18:18:31 +08:00
|
|
|
void __asan_describe_address(uptr addr) {
|
2014-07-22 05:33:46 +08:00
|
|
|
// Thread registry must be locked while we're describing an address.
|
|
|
|
asanThreadRegistry().Lock();
|
2015-04-23 04:30:19 +08:00
|
|
|
DescribeAddress(addr, 1, "");
|
2014-07-22 05:33:46 +08:00
|
|
|
asanThreadRegistry().Unlock();
|
2012-12-29 18:18:31 +08:00
|
|
|
}
|
|
|
|
|
2014-09-27 03:15:32 +08:00
|
|
|
int __asan_report_present() {
|
|
|
|
return report_happened ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr __asan_get_report_pc() {
|
|
|
|
return report_data.pc;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr __asan_get_report_bp() {
|
|
|
|
return report_data.bp;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr __asan_get_report_sp() {
|
|
|
|
return report_data.sp;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr __asan_get_report_address() {
|
|
|
|
return report_data.addr;
|
|
|
|
}
|
|
|
|
|
|
|
|
int __asan_get_report_access_type() {
|
|
|
|
return report_data.is_write ? 1 : 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uptr __asan_get_report_access_size() {
|
|
|
|
return report_data.access_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *__asan_get_report_description() {
|
|
|
|
return report_data.description;
|
|
|
|
}
|
|
|
|
|
2014-02-27 20:45:36 +08:00
|
|
|
extern "C" {
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
|
|
|
void __sanitizer_ptr_sub(void *a, void *b) {
|
|
|
|
CheckForInvalidPointerPair(a, b);
|
|
|
|
}
|
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE
|
|
|
|
void __sanitizer_ptr_cmp(void *a, void *b) {
|
|
|
|
CheckForInvalidPointerPair(a, b);
|
|
|
|
}
|
2015-10-01 08:22:21 +08:00
|
|
|
} // extern "C"
|
2014-02-27 20:45:36 +08:00
|
|
|
|
2012-12-08 06:01:28 +08:00
|
|
|
#if !SANITIZER_SUPPORTS_WEAK_HOOKS
|
2012-10-02 22:06:39 +08:00
|
|
|
// Provide default implementation of __asan_on_error that does nothing
|
|
|
|
// and may be overriden by user.
|
2013-08-13 19:42:45 +08:00
|
|
|
SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE NOINLINE
|
2012-10-02 22:06:39 +08:00
|
|
|
void __asan_on_error() {}
|
2012-12-08 06:01:28 +08:00
|
|
|
#endif
|