libsanitizer merge from upstream r221802

From-SVN: r217518
This commit is contained in:
Kostya Serebryany 2014-11-13 20:41:38 +00:00 committed by Kostya Serebryany
parent 47bf94b79a
commit c5be964a42
98 changed files with 2006 additions and 1222 deletions

View File

@ -1,3 +1,26 @@
2014-11-13 Kostya Serebryany <kcc@google.com>
* All source files: Merge from upstream r221802.
* sanitizer_common/sanitizer_symbolizer_libbacktrace.cc
(LibbacktraceSymbolizer::SymbolizeData): Replace 'address'
with 'start' to follow the new interface.
* asan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
* interception/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
* libbacktrace/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
* lsan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
* sanitizer_common/Makefile.am (sanitizer_common_files): Added new
files.
(AM_CXXFLAGS): Added -std=c++11.
* tsan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
* ubsan/Makefile.am (AM_CXXFLAGS): Added -std=c++11.
* asan/Makefile.in: Regenerate.
* interception/Makefile.in: Regenerate.
* libbacktrace/Makefile.in: Regenerate.
* lsan/Makefile.in: Regenerate.
* sanitizer_common/Makefile.in: Regenerate.
* tsan/Makefile.in: Regenerate.
* ubsan/Makefile.in: Regenerate.
2014-11-11 Francois-Xavier Coudert <fxcoudert@gcc.gnu.org>
PR target/63610

View File

@ -1,4 +1,4 @@
218156
221802
The first line of this file holds the svn revision number of the
last merge done from the master library sources.

View File

@ -9,6 +9,7 @@ DEFS += -DMAC_INTERPOSE_FUNCTIONS -DMISSING_BLOCKS_SUPPORT
endif
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros -fno-ipa-icf
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
AM_CXXFLAGS += -std=c++11
ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
toolexeclib_LTLIBRARIES = libasan.la

View File

@ -270,7 +270,7 @@ AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros -fno-ipa-icf \
$(LIBSTDCXX_RAW_CXX_CXXFLAGS)
$(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=c++11
ACLOCAL_AMFLAGS = -I $(top_srcdir) -I $(top_srcdir)/config
toolexeclib_LTLIBRARIES = libasan.la
nodist_toolexeclib_HEADERS = libasan_preinit.o

View File

@ -43,8 +43,8 @@ class AsanChunkView {
uptr AllocTid();
uptr FreeTid();
bool Eq(const AsanChunkView &c) const { return chunk_ == c.chunk_; }
void GetAllocStack(StackTrace *stack);
void GetFreeStack(StackTrace *stack);
StackTrace GetAllocStack();
StackTrace GetFreeStack();
bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
if (addr >= Beg() && (addr + access_size) <= End()) {
*offset = addr - Beg();
@ -137,20 +137,20 @@ struct AsanThreadLocalMallocStorage {
AsanThreadLocalMallocStorage() {}
};
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
AllocType alloc_type);
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type);
void asan_sized_free(void *ptr, uptr size, StackTrace *stack,
void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type);
void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack,
AllocType alloc_type);
void *asan_malloc(uptr size, StackTrace *stack);
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack);
void *asan_realloc(void *p, uptr size, StackTrace *stack);
void *asan_valloc(uptr size, StackTrace *stack);
void *asan_pvalloc(uptr size, StackTrace *stack);
void *asan_malloc(uptr size, BufferedStackTrace *stack);
void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack);
void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack);
void *asan_valloc(uptr size, BufferedStackTrace *stack);
void *asan_pvalloc(uptr size, BufferedStackTrace *stack);
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
StackTrace *stack);
BufferedStackTrace *stack);
uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp);
uptr asan_mz_size(const void *ptr);

View File

@ -180,20 +180,19 @@ uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
static void GetStackTraceFromId(u32 id, StackTrace *stack) {
static StackTrace GetStackTraceFromId(u32 id) {
CHECK(id);
uptr size = 0;
const uptr *trace = StackDepotGet(id, &size);
CHECK(trace);
stack->CopyFrom(trace, size);
StackTrace res = StackDepotGet(id);
CHECK(res.trace);
return res;
}
void AsanChunkView::GetAllocStack(StackTrace *stack) {
GetStackTraceFromId(chunk_->alloc_context_id, stack);
StackTrace AsanChunkView::GetAllocStack() {
return GetStackTraceFromId(chunk_->alloc_context_id);
}
void AsanChunkView::GetFreeStack(StackTrace *stack) {
GetStackTraceFromId(chunk_->free_context_id, stack);
StackTrace AsanChunkView::GetFreeStack() {
return GetStackTraceFromId(chunk_->free_context_id);
}
struct QuarantineCallback;
@ -261,7 +260,7 @@ void ReInitializeAllocator() {
quarantine.Init((uptr)flags()->quarantine_size, kMaxThreadLocalQuarantine);
}
static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
static void *Allocate(uptr size, uptr alignment, BufferedStackTrace *stack,
AllocType alloc_type, bool can_fill) {
if (UNLIKELY(!asan_inited))
AsanInitFromRtl();
@ -353,7 +352,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
meta[1] = chunk_beg;
}
m->alloc_context_id = StackDepotPut(stack->trace, stack->size);
m->alloc_context_id = StackDepotPut(*stack);
uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY);
// Unpoison the bulk of the memory region.
@ -389,15 +388,16 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack,
return res;
}
static void ReportInvalidFree(void *ptr, u8 chunk_state, StackTrace *stack) {
static void ReportInvalidFree(void *ptr, u8 chunk_state,
BufferedStackTrace *stack) {
if (chunk_state == CHUNK_QUARANTINE)
ReportDoubleFree((uptr)ptr, stack);
else
ReportFreeNotMalloced((uptr)ptr, stack);
}
static void AtomicallySetQuarantineFlag(AsanChunk *m,
void *ptr, StackTrace *stack) {
static void AtomicallySetQuarantineFlag(AsanChunk *m, void *ptr,
BufferedStackTrace *stack) {
u8 old_chunk_state = CHUNK_ALLOCATED;
// Flip the chunk_state atomically to avoid race on double-free.
if (!atomic_compare_exchange_strong((atomic_uint8_t*)m, &old_chunk_state,
@ -408,8 +408,8 @@ static void AtomicallySetQuarantineFlag(AsanChunk *m,
// Expects the chunk to already be marked as quarantined by using
// AtomicallySetQuarantineFlag.
static void QuarantineChunk(AsanChunk *m, void *ptr,
StackTrace *stack, AllocType alloc_type) {
static void QuarantineChunk(AsanChunk *m, void *ptr, BufferedStackTrace *stack,
AllocType alloc_type) {
CHECK_EQ(m->chunk_state, CHUNK_QUARANTINE);
if (m->alloc_type != alloc_type && flags()->alloc_dealloc_mismatch)
@ -421,7 +421,7 @@ static void QuarantineChunk(AsanChunk *m, void *ptr,
CHECK_EQ(m->free_tid, kInvalidTid);
AsanThread *t = GetCurrentThread();
m->free_tid = t ? t->tid() : 0;
m->free_context_id = StackDepotPut(stack->trace, stack->size);
m->free_context_id = StackDepotPut(*stack);
// Poison the region.
PoisonShadow(m->Beg(),
RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY),
@ -445,7 +445,7 @@ static void QuarantineChunk(AsanChunk *m, void *ptr,
}
}
static void Deallocate(void *ptr, uptr delete_size, StackTrace *stack,
static void Deallocate(void *ptr, uptr delete_size, BufferedStackTrace *stack,
AllocType alloc_type) {
uptr p = reinterpret_cast<uptr>(ptr);
if (p == 0) return;
@ -462,7 +462,8 @@ static void Deallocate(void *ptr, uptr delete_size, StackTrace *stack,
QuarantineChunk(m, ptr, stack, alloc_type);
}
static void *Reallocate(void *old_ptr, uptr new_size, StackTrace *stack) {
static void *Reallocate(void *old_ptr, uptr new_size,
BufferedStackTrace *stack) {
CHECK(old_ptr && new_size);
uptr p = reinterpret_cast<uptr>(old_ptr);
uptr chunk_beg = p - kChunkHeaderSize;
@ -575,25 +576,25 @@ void PrintInternalAllocatorStats() {
allocator.PrintStats();
}
void *asan_memalign(uptr alignment, uptr size, StackTrace *stack,
void *asan_memalign(uptr alignment, uptr size, BufferedStackTrace *stack,
AllocType alloc_type) {
return Allocate(size, alignment, stack, alloc_type, true);
}
void asan_free(void *ptr, StackTrace *stack, AllocType alloc_type) {
void asan_free(void *ptr, BufferedStackTrace *stack, AllocType alloc_type) {
Deallocate(ptr, 0, stack, alloc_type);
}
void asan_sized_free(void *ptr, uptr size, StackTrace *stack,
void asan_sized_free(void *ptr, uptr size, BufferedStackTrace *stack,
AllocType alloc_type) {
Deallocate(ptr, size, stack, alloc_type);
}
void *asan_malloc(uptr size, StackTrace *stack) {
void *asan_malloc(uptr size, BufferedStackTrace *stack) {
return Allocate(size, 8, stack, FROM_MALLOC, true);
}
void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
void *asan_calloc(uptr nmemb, uptr size, BufferedStackTrace *stack) {
if (CallocShouldReturnNullDueToOverflow(size, nmemb))
return AllocatorReturnNull();
void *ptr = Allocate(nmemb * size, 8, stack, FROM_MALLOC, false);
@ -604,7 +605,7 @@ void *asan_calloc(uptr nmemb, uptr size, StackTrace *stack) {
return ptr;
}
void *asan_realloc(void *p, uptr size, StackTrace *stack) {
void *asan_realloc(void *p, uptr size, BufferedStackTrace *stack) {
if (p == 0)
return Allocate(size, 8, stack, FROM_MALLOC, true);
if (size == 0) {
@ -614,11 +615,11 @@ void *asan_realloc(void *p, uptr size, StackTrace *stack) {
return Reallocate(p, size, stack);
}
void *asan_valloc(uptr size, StackTrace *stack) {
void *asan_valloc(uptr size, BufferedStackTrace *stack) {
return Allocate(size, GetPageSizeCached(), stack, FROM_MALLOC, true);
}
void *asan_pvalloc(uptr size, StackTrace *stack) {
void *asan_pvalloc(uptr size, BufferedStackTrace *stack) {
uptr PageSize = GetPageSizeCached();
size = RoundUpTo(size, PageSize);
if (size == 0) {
@ -629,7 +630,7 @@ void *asan_pvalloc(uptr size, StackTrace *stack) {
}
int asan_posix_memalign(void **memptr, uptr alignment, uptr size,
StackTrace *stack) {
BufferedStackTrace *stack) {
void *ptr = Allocate(size, alignment, stack, FROM_MALLOC, true);
CHECK(IsAligned((uptr)ptr, alignment));
*memptr = ptr;

View File

@ -15,31 +15,88 @@
#include "asan_flags.h"
#include "asan_internal.h"
#include "asan_mapping.h"
#include "asan_report.h"
#include "asan_thread.h"
namespace __asan {
void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
descr->name[0] = 0;
descr->region_address = 0;
descr->region_size = 0;
descr->region_kind = "stack";
AsanThread::StackFrameAccess access;
if (!t->GetStackFrameAccessByAddr(addr, &access))
return;
InternalMmapVector<StackVarDescr> vars(16);
if (!ParseFrameDescription(access.frame_descr, &vars)) {
return;
}
for (uptr i = 0; i < vars.size(); i++) {
if (access.offset <= vars[i].beg + vars[i].size) {
internal_strncat(descr->name, vars[i].name_pos,
Min(descr->name_size, vars[i].name_len));
descr->region_address = addr - (access.offset - vars[i].beg);
descr->region_size = vars[i].size;
return;
}
}
}
void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
descr->name[0] = 0;
descr->region_address = 0;
descr->region_size = 0;
if (!chunk.IsValid()) {
descr->region_kind = "heap-invalid";
return;
}
descr->region_address = chunk.Beg();
descr->region_size = chunk.UsedSize();
descr->region_kind = "heap";
}
void AsanLocateAddress(uptr addr, AddressDescription *descr) {
if (DescribeAddressIfShadow(addr, descr, /* print */ false)) {
return;
}
if (GetInfoForAddressIfGlobal(addr, descr)) {
return;
}
asanThreadRegistry().Lock();
AsanThread *thread = FindThreadByStackAddress(addr);
asanThreadRegistry().Unlock();
if (thread) {
GetInfoForStackVar(addr, descr, thread);
return;
}
GetInfoForHeapAddress(addr, descr);
}
uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
bool alloc_stack) {
AsanChunkView chunk = FindHeapChunkByAddress(addr);
if (!chunk.IsValid()) return 0;
StackTrace stack;
StackTrace stack(nullptr, 0);
if (alloc_stack) {
if (chunk.AllocTid() == kInvalidTid) return 0;
chunk.GetAllocStack(&stack);
stack = chunk.GetAllocStack();
if (thread_id) *thread_id = chunk.AllocTid();
} else {
if (chunk.FreeTid() == kInvalidTid) return 0;
chunk.GetFreeStack(&stack);
stack = chunk.GetFreeStack();
if (thread_id) *thread_id = chunk.FreeTid();
}
if (trace && size) {
if (size > kStackTraceMax)
size = kStackTraceMax;
if (size > stack.size)
size = stack.size;
size = Min(size, Min(stack.size, kStackTraceMax));
for (uptr i = 0; i < size; i++)
trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
@ -53,6 +110,16 @@ uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
using namespace __asan;
SANITIZER_INTERFACE_ATTRIBUTE
const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
uptr *region_address, uptr *region_size) {
AddressDescription descr = { name, name_size, 0, 0, 0 };
AsanLocateAddress(addr, &descr);
if (region_address) *region_address = descr.region_address;
if (region_size) *region_size = descr.region_size;
return descr.region_kind;
}
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);

View File

@ -63,6 +63,7 @@ struct Flags {
int detect_invalid_pointer_pairs;
bool detect_container_overflow;
int detect_odr_violation;
bool dump_instruction_bytes;
};
extern Flags asan_flags_dont_use_directly;

View File

@ -69,6 +69,14 @@ ALWAYS_INLINE void PoisonRedZones(const Global &g) {
}
}
const uptr kMinimalDistanceFromAnotherGlobal = 64;
bool IsAddressNearGlobal(uptr addr, const __asan_global &g) {
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
if (addr >= g.beg + g.size_with_redzone) return false;
return true;
}
static void ReportGlobal(const Global &g, const char *prefix) {
Report("%s Global[%p]: beg=%p size=%zu/%zu name=%s module=%s dyn_init=%zu\n",
prefix, &g, (void *)g.beg, g.size, g.size_with_redzone, g.name,
@ -80,19 +88,45 @@ static void ReportGlobal(const Global &g, const char *prefix) {
}
}
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
static bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print,
Global *output_global) {
if (!flags()->report_globals) return false;
BlockingMutexLock lock(&mu_for_globals);
bool res = false;
for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
const Global &g = *l->g;
if (flags()->report_globals >= 2)
ReportGlobal(g, "Search");
res |= DescribeAddressRelativeToGlobal(addr, size, g);
if (print) {
if (flags()->report_globals >= 2)
ReportGlobal(g, "Search");
res |= DescribeAddressRelativeToGlobal(addr, size, g);
} else {
if (IsAddressNearGlobal(addr, g)) {
CHECK(output_global);
*output_global = g;
return true;
}
}
}
return res;
}
bool DescribeAddressIfGlobal(uptr addr, uptr size) {
return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true,
/* output_global */ nullptr);
}
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
Global g = {};
if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) {
internal_strncpy(descr->name, g.name, descr->name_size);
descr->region_address = g.beg;
descr->region_size = g.size;
descr->region_kind = "global";
return true;
}
return false;
}
u32 FindRegistrationSite(const Global *g) {
CHECK(global_registration_site_vector);
for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) {
@ -181,7 +215,7 @@ using namespace __asan; // NOLINT
void __asan_register_globals(__asan_global *globals, uptr n) {
if (!flags()->report_globals) return;
GET_STACK_TRACE_FATAL_HERE;
u32 stack_id = StackDepotPut(stack.trace, stack.size);
u32 stack_id = StackDepotPut(stack);
BlockingMutexLock lock(&mu_for_globals);
if (!global_registration_site_vector)
global_registration_site_vector =

View File

@ -88,6 +88,28 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_describe_address(uptr addr);
SANITIZER_INTERFACE_ATTRIBUTE
int __asan_report_present();
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_report_pc();
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_report_bp();
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_report_sp();
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_report_address();
SANITIZER_INTERFACE_ATTRIBUTE
int __asan_get_report_access_type();
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_report_access_size();
SANITIZER_INTERFACE_ATTRIBUTE
const char * __asan_get_report_description();
SANITIZER_INTERFACE_ATTRIBUTE
const char * __asan_locate_address(uptr addr, char *name, uptr name_size,
uptr *region_address, uptr *region_size);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
u32 *thread_id);
@ -149,6 +171,10 @@ extern "C" {
void __asan_poison_cxx_array_cookie(uptr p);
SANITIZER_INTERFACE_ATTRIBUTE
uptr __asan_load_cxx_array_cookie(uptr *p);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_poison_intra_object_redzone(uptr p, uptr size);
SANITIZER_INTERFACE_ATTRIBUTE
void __asan_unpoison_intra_object_redzone(uptr p, uptr size);
} // extern "C"
#endif // ASAN_INTERFACE_INTERNAL_H

View File

@ -133,6 +133,7 @@ const int kAsanStackUseAfterScopeMagic = 0xf8;
const int kAsanGlobalRedzoneMagic = 0xf9;
const int kAsanInternalHeapMagic = 0xfe;
const int kAsanArrayCookieMagic = 0xac;
const int kAsanIntraObjectRedzone = 0xbb;
static const uptr kCurrentStackFrameMagic = 0x41B58AB3;
static const uptr kRetiredStackFrameMagic = 0x45E0360E;

View File

@ -295,7 +295,7 @@ using namespace __asan; // NOLINT
// The caller retains control of the allocated context.
extern "C"
asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func,
StackTrace *stack) {
BufferedStackTrace *stack) {
asan_block_context_t *asan_ctxt =
(asan_block_context_t*) asan_malloc(sizeof(asan_block_context_t), stack);
asan_ctxt->block = ctxt;

View File

@ -58,11 +58,11 @@
// || `[0x00000000, 0x1fffffff]` || LowMem ||
//
// Default Linux/MIPS mapping:
// || `[0x2aaa8000, 0xffffffff]` || HighMem ||
// || `[0x0fffd000, 0x2aaa7fff]` || HighShadow ||
// || `[0x0bffd000, 0x0fffcfff]` || ShadowGap ||
// || `[0x0aaa8000, 0x0bffcfff]` || LowShadow ||
// || `[0x00000000, 0x0aaa7fff]` || LowMem ||
// || `[0x2aaa0000, 0xffffffff]` || HighMem ||
// || `[0x0fff4000, 0x2aa9ffff]` || HighShadow ||
// || `[0x0bff4000, 0x0fff3fff]` || ShadowGap ||
// || `[0x0aaa0000, 0x0bff3fff]` || LowShadow ||
// || `[0x00000000, 0x0aa9ffff]` || LowMem ||
//
// Shadow mapping on FreeBSD/x86-64 with SHADOW_OFFSET == 0x400000000000:
// || `[0x500000000000, 0x7fffffffffff]` || HighMem ||
@ -84,7 +84,8 @@ static const u64 kIosShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kDefaultShadowOffset64 = 1ULL << 44;
static const u64 kDefaultShort64bitShadowOffset = 0x7FFF8000; // < 2G.
static const u64 kAArch64_ShadowOffset64 = 1ULL << 36;
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa8000;
static const u64 kMIPS32_ShadowOffset32 = 0x0aaa0000;
static const u64 kMIPS64_ShadowOffset64 = 1ULL << 36;
static const u64 kPPC64_ShadowOffset64 = 1ULL << 41;
static const u64 kFreeBSD_ShadowOffset32 = 1ULL << 30; // 0x40000000
static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
@ -114,6 +115,8 @@ static const u64 kFreeBSD_ShadowOffset64 = 1ULL << 46; // 0x400000000000
# define SHADOW_OFFSET kFreeBSD_ShadowOffset64
# elif SANITIZER_MAC
# define SHADOW_OFFSET kDefaultShadowOffset64
# elif defined(__mips64)
# define SHADOW_OFFSET kMIPS64_ShadowOffset64
# else
# define SHADOW_OFFSET kDefaultShort64bitShadowOffset
# endif

View File

@ -59,6 +59,27 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {
FlushUnneededShadowMemory(shadow_beg, shadow_end - shadow_beg);
}
void AsanPoisonOrUnpoisonIntraObjectRedzone(uptr ptr, uptr size, bool poison) {
uptr end = ptr + size;
if (common_flags()->verbosity) {
Printf("__asan_%spoison_intra_object_redzone [%p,%p) %zd\n",
poison ? "" : "un", ptr, end, size);
if (common_flags()->verbosity >= 2)
PRINT_CURRENT_STACK();
}
CHECK(size);
CHECK_LE(size, 4096);
CHECK(IsAligned(end, SHADOW_GRANULARITY));
if (!IsAligned(ptr, SHADOW_GRANULARITY)) {
*(u8 *)MemToShadow(ptr) =
poison ? static_cast<u8>(ptr % SHADOW_GRANULARITY) : 0;
ptr |= SHADOW_GRANULARITY - 1;
ptr++;
}
for (; ptr < end; ptr += SHADOW_GRANULARITY)
*(u8*)MemToShadow(ptr) = poison ? kAsanIntraObjectRedzone : 0;
}
} // namespace __asan
// ---------------------- Interface ---------------- {{{1
@ -250,7 +271,8 @@ uptr __asan_load_cxx_array_cookie(uptr *p) {
"expect a double-free report\n");
return 0;
}
// FIXME: apparently it can be something else; need to find a reproducer.
// The cookie may remain unpoisoned if e.g. it comes from a custom
// operator new defined inside a class.
return *p;
}
@ -372,6 +394,17 @@ int __sanitizer_verify_contiguous_container(const void *beg_p,
return 0;
return 1;
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __asan_poison_intra_object_redzone(uptr ptr, uptr size) {
AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, true);
}
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
void __asan_unpoison_intra_object_redzone(uptr ptr, uptr size) {
AsanPoisonOrUnpoisonIntraObjectRedzone(ptr, size, false);
}
// --- Implementation of LSan-specific functions --- {{{1
namespace __lsan {
bool WordIsPoisoned(uptr addr) {

View File

@ -31,6 +31,7 @@
namespace __asan {
void AsanOnSIGSEGV(int, void *siginfo, void *context) {
ScopedDeadlySignal signal_scope(GetCurrentThread());
uptr addr = (uptr)((siginfo_t*)siginfo)->si_addr;
int code = (int)((siginfo_t*)siginfo)->si_code;
// Write the first message using the bullet-proof write.
@ -39,12 +40,12 @@ void AsanOnSIGSEGV(int, void *siginfo, void *context) {
GetPcSpBp(context, &pc, &sp, &bp);
// Access at a reasonable offset above SP, or slightly below it (to account
// for x86_64 redzone, ARM push of multiple registers, etc) is probably a
// stack overflow.
// for x86_64 or PowerPC redzone, ARM push of multiple registers, etc) is
// probably a stack overflow.
// We also check si_code to filter out SEGV caused by something else other
// then hitting the guard page or unmapped memory, like, for example,
// unaligned memory access.
if (addr + 128 > sp && addr < sp + 0xFFFF &&
if (addr + 512 > sp && addr < sp + 0xFFFF &&
(code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
ReportStackOverflow(pc, sp, bp, context, addr);
else

View File

@ -29,6 +29,19 @@ static char *error_message_buffer = 0;
static uptr error_message_buffer_pos = 0;
static uptr error_message_buffer_size = 0;
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 = {};
void AppendToErrorMessageBuffer(const char *buffer) {
if (error_message_buffer) {
uptr length = internal_strlen(buffer);
@ -79,20 +92,31 @@ class Decorator: public __sanitizer::SanitizerCommonDecorator {
return Red();
case kAsanInternalHeapMagic:
return Yellow();
case kAsanIntraObjectRedzone:
return Yellow();
default:
return Default();
}
}
const char *EndShadowByte() { return Default(); }
const char *MemoryByte() { return Magenta(); }
const char *EndMemoryByte() { return Default(); }
};
// ---------------------- Helper functions ----------------------- {{{1
static void PrintShadowByte(InternalScopedString *str, const char *before,
u8 byte, const char *after = "\n") {
static void PrintMemoryByte(InternalScopedString *str, const char *before,
u8 byte, bool in_shadow, const char *after = "\n") {
Decorator d;
str->append("%s%s%x%x%s%s", before, d.ShadowByte(byte), byte >> 4, byte & 15,
d.EndShadowByte(), after);
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);
}
static void PrintShadowBytes(InternalScopedString *str, const char *before,
@ -144,9 +168,27 @@ static void PrintLegend(InternalScopedString *str) {
kAsanContiguousContainerOOBMagic);
PrintShadowByte(str, " Array cookie: ",
kAsanArrayCookieMagic);
PrintShadowByte(str, " Intra object redzone: ",
kAsanIntraObjectRedzone);
PrintShadowByte(str, " ASan internal: ", kAsanInternalHeapMagic);
}
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());
}
static void PrintShadowMemoryForAddress(uptr addr) {
if (!AddrIsInMem(addr)) return;
uptr shadow_addr = MemToShadow(addr);
@ -235,9 +277,7 @@ static void PrintGlobalLocation(InternalScopedString *str,
bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
const __asan_global &g) {
static const uptr kMinimalDistanceFromAnotherGlobal = 64;
if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
if (addr >= g.beg + g.size_with_redzone) return false;
if (!IsAddressNearGlobal(addr, g)) return false;
InternalScopedString str(4096);
Decorator d;
str.append("%s", d.Location());
@ -263,21 +303,20 @@ bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
return true;
}
bool DescribeAddressIfShadow(uptr addr) {
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr, bool print) {
if (AddrIsInMem(addr))
return false;
static const char kAddrInShadowReport[] =
"Address %p is located in the %s.\n";
if (AddrIsInShadowGap(addr)) {
Printf(kAddrInShadowReport, addr, "shadow gap area");
return true;
}
if (AddrIsInHighShadow(addr)) {
Printf(kAddrInShadowReport, addr, "high shadow area");
return true;
}
if (AddrIsInLowShadow(addr)) {
Printf(kAddrInShadowReport, addr, "low shadow area");
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;
}
return true;
}
CHECK(0 && "Address is not in memory and not in shadow?");
@ -304,16 +343,15 @@ 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;
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;
uptr addr_end = addr + access_size;
const char *pos_descr = 0;
// If the variable [var_beg, var_end) is the nearest variable to the
// 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 >= 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)
@ -322,14 +360,20 @@ void PrintAccessAndVarIntersection(const char *var_name,
next_var_beg - addr_end >= addr - var_end)
pos_descr = "overflows";
} else {
if (addr_end > var_beg)
if (addr_end > var.beg)
pos_descr = "partially underflows";
else if (addr >= prev_var_end &&
addr - prev_var_end >= var_beg - addr_end)
addr - prev_var_end >= var.beg - addr_end)
pos_descr = "underflows";
}
InternalScopedString str(1024);
str.append(" [%zd, %zd) '%s'", var_beg, var_beg + var_size, var_name);
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("'");
if (pos_descr) {
Decorator d;
// FIXME: we may want to also print the size of the access here,
@ -344,9 +388,14 @@ void PrintAccessAndVarIntersection(const char *var_name,
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars) {
CHECK(frame_descr);
char *p;
// 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".
uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
CHECK_GT(n_objects, 0);
if (n_objects == 0)
return false;
for (uptr i = 0; i < n_objects; i++) {
uptr beg = (uptr)internal_simple_strtoll(p, &p, 10);
@ -367,31 +416,21 @@ bool ParseFrameDescription(const char *frame_descr,
bool DescribeAddressIfStack(uptr addr, uptr access_size) {
AsanThread *t = FindThreadByStackAddress(addr);
if (!t) return false;
const uptr kBufSize = 4095;
char buf[kBufSize];
uptr offset = 0;
uptr frame_pc = 0;
char tname[128];
const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc);
#ifdef __powerpc64__
// On PowerPC64, the address of a function actually points to a
// three-doubleword data structure with the first field containing
// the address of the function's code.
frame_pc = *reinterpret_cast<uptr *>(frame_pc);
#endif
// 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 ".
CHECK(frame_descr);
Decorator d;
char tname[128];
Printf("%s", d.Location());
Printf("Address %p is located in stack of thread T%d%s "
"at offset %zu in frame\n",
addr, t->tid(),
ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)),
offset);
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());
// 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.
@ -399,16 +438,21 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
// 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).
StackTrace alloca_stack;
alloca_stack.trace[0] = frame_pc + 16;
alloca_stack.size = 1;
#if defined(__powerpc64__) && defined(__BIG_ENDIAN__)
// On PowerPC64 ELFv1, the address of a function actually points to a
// 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
access.frame_pc += 16;
Printf("%s", d.EndLocation());
StackTrace alloca_stack(&access.frame_pc, 1);
alloca_stack.Print();
InternalMmapVector<StackVarDescr> vars(16);
if (!ParseFrameDescription(frame_descr, &vars)) {
if (!ParseFrameDescription(access.frame_descr, &vars)) {
Printf("AddressSanitizer can't parse the stack frame "
"descriptor: |%s|\n", frame_descr);
"descriptor: |%s|\n", access.frame_descr);
// 'addr' is a stack address, so return true even if we can't parse frame
return true;
}
@ -418,13 +462,9 @@ bool DescribeAddressIfStack(uptr addr, uptr access_size) {
// Report all objects in this frame.
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,
PrintAccessAndVarIntersection(vars[i], access.offset, access_size,
prev_var_end, next_var_beg);
}
Printf("HINT: this may be a false positive if your program uses "
@ -476,8 +516,7 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
asanThreadRegistry().CheckLocked();
AsanThreadContext *alloc_thread =
GetThreadContextByTidLocked(chunk.AllocTid());
StackTrace alloc_stack;
chunk.GetAllocStack(&alloc_stack);
StackTrace alloc_stack = chunk.GetAllocStack();
char tname[128];
Decorator d;
AsanThreadContext *free_thread = 0;
@ -487,8 +526,7 @@ void DescribeHeapAddress(uptr addr, uptr access_size) {
free_thread->tid,
ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
d.EndAllocation());
StackTrace free_stack;
chunk.GetFreeStack(&free_stack);
StackTrace free_stack = chunk.GetFreeStack();
free_stack.Print();
Printf("%spreviously allocated by thread T%d%s here:%s\n",
d.Allocation(), alloc_thread->tid,
@ -538,9 +576,7 @@ void DescribeThread(AsanThreadContext *context) {
" created by T%d%s here:\n", context->parent_tid,
ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname)));
Printf("%s", str.data());
uptr stack_size;
const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size);
StackTrace::PrintStack(stack_trace, stack_size);
StackDepotGet(context->stack_id).Print();
// Recursively described parent thread if needed.
if (flags()->print_full_thread_history) {
AsanThreadContext *parent_context =
@ -555,7 +591,7 @@ void DescribeThread(AsanThreadContext *context) {
// immediately after printing error report.
class ScopedInErrorReport {
public:
ScopedInErrorReport() {
explicit ScopedInErrorReport(ReportData *report = nullptr) {
static atomic_uint32_t num_calls;
static u32 reporting_thread_tid;
if (atomic_fetch_add(&num_calls, 1, memory_order_relaxed) != 0) {
@ -575,6 +611,8 @@ class ScopedInErrorReport {
// Die() to bypass any additional checks.
internal__exit(flags()->exitcode);
}
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.
@ -634,11 +672,12 @@ void ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
Printf("%s", d.EndWarning());
GET_STACK_TRACE_SIGNAL(pc, bp, context);
stack.Print();
MaybeDumpInstructionBytes(pc);
Printf("AddressSanitizer can not provide additional info.\n");
ReportErrorSummary("SEGV", &stack);
}
void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@ -657,7 +696,7 @@ void ReportDoubleFree(uptr addr, StackTrace *free_stack) {
}
void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
StackTrace *free_stack) {
BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@ -680,7 +719,7 @@ void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
"ASAN_OPTIONS=new_delete_type_mismatch=0\n");
}
void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@ -697,7 +736,7 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) {
ReportErrorSummary("bad-free", &stack);
}
void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType alloc_type,
AllocType dealloc_type) {
static const char *alloc_names[] =
@ -720,7 +759,7 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
"ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
}
void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@ -733,7 +772,8 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) {
ReportErrorSummary("bad-malloc_usable_size", stack);
}
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
Printf("%s", d.Warning());
@ -746,9 +786,10 @@ void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) {
ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
}
void ReportStringFunctionMemoryRangesOverlap(
const char *function, const char *offset1, uptr length1,
const char *offset2, uptr length2, StackTrace *stack) {
void ReportStringFunctionMemoryRangesOverlap(const char *function,
const char *offset1, uptr length1,
const char *offset2, uptr length2,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
char bug_type[100];
@ -765,7 +806,7 @@ void ReportStringFunctionMemoryRangesOverlap(
}
void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
StackTrace *stack) {
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Decorator d;
const char *bug_type = "negative-size-param";
@ -779,7 +820,7 @@ void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
uptr old_mid, uptr new_mid,
StackTrace *stack) {
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Report("ERROR: AddressSanitizer: bad parameters to "
"__sanitizer_annotate_contiguous_container:\n"
@ -809,12 +850,9 @@ void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
if (stack_id1 && stack_id2) {
Printf("These globals were registered at these points:\n");
Printf(" [1]:\n");
uptr stack_size;
const uptr *stack_trace = StackDepotGet(stack_id1, &stack_size);
StackTrace::PrintStack(stack_trace, stack_size);
StackDepotGet(stack_id1).Print();
Printf(" [2]:\n");
stack_trace = StackDepotGet(stack_id2, &stack_size);
StackTrace::PrintStack(stack_trace, stack_size);
StackDepotGet(stack_id2).Print();
}
Report("HINT: if you don't care about these warnings you may set "
"ASAN_OPTIONS=detect_odr_violation=0\n");
@ -854,8 +892,8 @@ static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
}
// ----------------------- Mac-specific reports ----------------- {{{1
void WarnMacFreeUnallocated(
uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
BufferedStackTrace *stack) {
// Just print a warning here.
Printf("free_common(%p) -- attempting to free unallocated memory.\n"
"AddressSanitizer is ignoring this error on Mac OS now.\n",
@ -865,8 +903,8 @@ void WarnMacFreeUnallocated(
DescribeHeapAddress(addr, 1);
}
void ReportMacMzReallocUnknown(
uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
void ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Printf("mz_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n",
@ -876,8 +914,8 @@ void ReportMacMzReallocUnknown(
DescribeHeapAddress(addr, 1);
}
void ReportMacCfReallocUnknown(
uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack) {
void ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr, const char *zone_name,
BufferedStackTrace *stack) {
ScopedInErrorReport in_report;
Printf("cf_realloc(%p) -- attempting to realloc unallocated memory.\n"
"This is an unrecoverable problem, exiting now.\n",
@ -894,8 +932,6 @@ using namespace __asan; // NOLINT
void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
uptr access_size) {
ScopedInErrorReport in_report;
// Determine the error type.
const char *bug_descr = "unknown-crash";
if (AddrIsInMem(addr)) {
@ -941,8 +977,16 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write,
case kAsanGlobalRedzoneMagic:
bug_descr = "global-buffer-overflow";
break;
case kAsanIntraObjectRedzone:
bug_descr = "intra-object-overflow";
break;
}
}
ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
bug_descr };
ScopedInErrorReport in_report(&report);
Decorator d;
Printf("%s", d.Warning());
Report("ERROR: AddressSanitizer: %s on address "
@ -984,6 +1028,38 @@ void __asan_describe_address(uptr addr) {
asanThreadRegistry().Unlock();
}
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;
}
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_ptr_sub(void *a, void *b) {

View File

@ -23,13 +23,24 @@ struct StackVarDescr {
uptr name_len;
};
struct AddressDescription {
char *name;
uptr name_size;
uptr region_address;
uptr region_size;
const char *region_kind;
};
// The following functions prints address description depending
// on the memory type (shadow/heap/stack/global).
void DescribeHeapAddress(uptr addr, uptr access_size);
bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
const __asan_global &g);
bool DescribeAddressIfShadow(uptr addr);
bool IsAddressNearGlobal(uptr addr, const __asan_global &g);
bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
bool DescribeAddressIfShadow(uptr addr, AddressDescription *descr = nullptr,
bool print = true);
bool ParseFrameDescription(const char *frame_descr,
InternalMmapVector<StackVarDescr> *vars);
bool DescribeAddressIfStack(uptr addr, uptr access_size);
@ -44,35 +55,41 @@ void NORETURN
void NORETURN ReportSIGSEGV(const char *description, uptr pc, uptr sp, uptr bp,
void *context, uptr addr);
void NORETURN ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
StackTrace *free_stack);
void NORETURN ReportDoubleFree(uptr addr, StackTrace *free_stack);
void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *free_stack);
void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack,
BufferedStackTrace *free_stack);
void NORETURN ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
void NORETURN ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
void NORETURN ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
AllocType alloc_type,
AllocType dealloc_type);
void NORETURN ReportMallocUsableSizeNotOwned(uptr addr,
StackTrace *stack);
void NORETURN
ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack);
void NORETURN ReportStringFunctionMemoryRangesOverlap(
const char *function, const char *offset1, uptr length1,
const char *offset2, uptr length2, StackTrace *stack);
ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack);
void NORETURN
ReportStringFunctionSizeOverflow(uptr offset, uptr size, StackTrace *stack);
ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
BufferedStackTrace *stack);
void NORETURN
ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end, uptr old_mid,
uptr new_mid, StackTrace *stack);
ReportStringFunctionMemoryRangesOverlap(const char *function,
const char *offset1, uptr length1,
const char *offset2, uptr length2,
BufferedStackTrace *stack);
void NORETURN ReportStringFunctionSizeOverflow(uptr offset, uptr size,
BufferedStackTrace *stack);
void NORETURN
ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
uptr old_mid, uptr new_mid,
BufferedStackTrace *stack);
void NORETURN
ReportODRViolation(const __asan_global *g1, u32 stack_id1,
const __asan_global *g2, u32 stack_id2);
// Mac-specific errors and warnings.
void WarnMacFreeUnallocated(
uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
void NORETURN ReportMacMzReallocUnknown(
uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
void NORETURN ReportMacCfReallocUnknown(
uptr addr, uptr zone_ptr, const char *zone_name, StackTrace *stack);
void WarnMacFreeUnallocated(uptr addr, uptr zone_ptr, const char *zone_name,
BufferedStackTrace *stack);
void NORETURN ReportMacMzReallocUnknown(uptr addr, uptr zone_ptr,
const char *zone_name,
BufferedStackTrace *stack);
void NORETURN ReportMacCfReallocUnknown(uptr addr, uptr zone_ptr,
const char *zone_name,
BufferedStackTrace *stack);
} // namespace __asan

View File

@ -65,7 +65,7 @@ static void AsanCheckFailed(const char *file, int line, const char *cond,
Report("AddressSanitizer CHECK failed: %s:%d \"%s\" (0x%zx, 0x%zx)\n", file,
line, cond, (uptr)v1, (uptr)v2);
// FIXME: check for infinite recursion without a thread-local counter here.
PRINT_CURRENT_STACK();
PRINT_CURRENT_STACK_CHECK();
Die();
}
@ -228,6 +228,9 @@ static void ParseFlagsFromString(Flags *f, const char *str) {
"If >=2, detect violation of One-Definition-Rule (ODR); "
"If ==1, detect ODR-violation only if the two variables "
"have different sizes");
ParseFlag(str, &f->dump_instruction_bytes, "dump_instruction_bytes",
"If true, dump 16 bytes starting at the instruction that caused SEGV");
}
void InitializeFlags(Flags *f, const char *env) {
@ -281,6 +284,7 @@ void InitializeFlags(Flags *f, const char *env) {
f->detect_invalid_pointer_pairs = 0;
f->detect_container_overflow = true;
f->detect_odr_violation = 2;
f->dump_instruction_bytes = false;
// Override from compile definition.
ParseFlagsFromString(f, MaybeUseAsanDefaultOptionsCompileDefinition());

View File

@ -23,8 +23,9 @@ namespace __asan {
// The pc will be in the position 0 of the resulting stack trace.
// The bp may refer to the current frame or to the caller's frame.
ALWAYS_INLINE
void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
uptr bp, void *context, bool fast) {
void GetStackTraceWithPcBpAndContext(BufferedStackTrace *stack, uptr max_depth,
uptr pc, uptr bp, void *context,
bool fast) {
#if SANITIZER_WINDOWS
stack->Unwind(max_depth, pc, bp, context, 0, 0, fast);
#else
@ -32,6 +33,10 @@ void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
stack->size = 0;
if (LIKELY(asan_inited)) {
if ((t = GetCurrentThread()) && !t->isUnwinding()) {
// On FreeBSD the slow unwinding that leverages _Unwind_Backtrace()
// yields the call stack of the signal's handler and not of the code
// that raised the signal (as it does on Linux).
if (SANITIZER_FREEBSD && t->isInDeadlySignal()) fast = true;
uptr stack_top = t->stack_top();
uptr stack_bottom = t->stack_bottom();
ScopedUnwinding unwind_scope(t);
@ -51,14 +56,14 @@ void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
// don't want stack trace to contain functions from ASan internals.
#define GET_STACK_TRACE(max_size, fast) \
StackTrace stack; \
BufferedStackTrace stack; \
if (max_size <= 2) { \
stack.size = max_size; \
if (max_size > 0) { \
stack.top_frame_bp = GET_CURRENT_FRAME(); \
stack.trace[0] = StackTrace::GetCurrentPc(); \
stack.trace_buffer[0] = StackTrace::GetCurrentPc(); \
if (max_size > 1) \
stack.trace[1] = GET_CALLER_PC(); \
stack.trace_buffer[1] = GET_CALLER_PC(); \
} \
} else { \
GetStackTraceWithPcBpAndContext(&stack, max_size, \
@ -67,18 +72,21 @@ void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
}
#define GET_STACK_TRACE_FATAL(pc, bp) \
StackTrace stack; \
BufferedStackTrace stack; \
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, 0, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_SIGNAL(pc, bp, context) \
StackTrace stack; \
BufferedStackTrace stack; \
GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context, \
common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_FATAL_HERE \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_fatal)
#define GET_STACK_TRACE_CHECK_HERE \
GET_STACK_TRACE(kStackTraceMax, common_flags()->fast_unwind_on_check)
#define GET_STACK_TRACE_THREAD \
GET_STACK_TRACE(kStackTraceMax, true)
@ -94,4 +102,10 @@ void GetStackTraceWithPcBpAndContext(StackTrace *stack, uptr max_depth, uptr pc,
stack.Print(); \
}
#define PRINT_CURRENT_STACK_CHECK() \
{ \
GET_STACK_TRACE_CHECK_HERE; \
stack.Print(); \
}
#endif // ASAN_STACK_H

View File

@ -28,7 +28,7 @@ namespace __asan {
void AsanThreadContext::OnCreated(void *arg) {
CreateThreadContextArgs *args = static_cast<CreateThreadContextArgs*>(arg);
if (args->stack)
stack_id = StackDepotPut(args->stack->trace, args->stack->size);
stack_id = StackDepotPut(*args->stack);
thread = args->thread;
thread->set_context(this);
}
@ -196,17 +196,18 @@ void AsanThread::ClearShadowForThreadStackAndTLS() {
PoisonShadow(tls_begin_, tls_end_ - tls_begin_, 0);
}
const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
uptr *frame_pc) {
bool AsanThread::GetStackFrameAccessByAddr(uptr addr,
StackFrameAccess *access) {
uptr bottom = 0;
if (AddrIsInStack(addr)) {
bottom = stack_bottom();
} else if (has_fake_stack()) {
bottom = fake_stack()->AddrIsInFakeStack(addr);
CHECK(bottom);
*offset = addr - bottom;
*frame_pc = ((uptr*)bottom)[2];
return (const char *)((uptr*)bottom)[1];
access->offset = addr - bottom;
access->frame_pc = ((uptr*)bottom)[2];
access->frame_descr = (const char *)((uptr*)bottom)[1];
return true;
}
uptr aligned_addr = addr & ~(SANITIZER_WORDSIZE/8 - 1); // align addr.
u8 *shadow_ptr = (u8*)MemToShadow(aligned_addr);
@ -223,15 +224,15 @@ const char *AsanThread::GetFrameNameByAddr(uptr addr, uptr *offset,
}
if (shadow_ptr < shadow_bottom) {
*offset = 0;
return "UNKNOWN";
return false;
}
uptr* ptr = (uptr*)SHADOW_TO_MEM((uptr)(shadow_ptr + 1));
CHECK(ptr[0] == kCurrentStackFrameMagic);
*offset = addr - (uptr)ptr;
*frame_pc = ptr[2];
return (const char*)ptr[1];
access->offset = addr - (uptr)ptr;
access->frame_pc = ptr[2];
access->frame_descr = (const char*)ptr[1];
return true;
}
static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,

View File

@ -69,7 +69,12 @@ class AsanThread {
AsanThreadContext *context() { return context_; }
void set_context(AsanThreadContext *context) { context_ = context; }
const char *GetFrameNameByAddr(uptr addr, uptr *offset, uptr *frame_pc);
struct StackFrameAccess {
uptr offset;
uptr frame_pc;
const char *frame_descr;
};
bool GetStackFrameAccessByAddr(uptr addr, StackFrameAccess *access);
bool AddrIsInStack(uptr addr) {
return addr >= stack_bottom_ && addr < stack_top_;
@ -101,6 +106,10 @@ class AsanThread {
bool isUnwinding() const { return unwinding_; }
void setUnwinding(bool b) { unwinding_ = b; }
// True if we are in a deadly signal handler.
bool isInDeadlySignal() const { return in_deadly_signal_; }
void setInDeadlySignal(bool b) { in_deadly_signal_ = b; }
AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; }
AsanStats &stats() { return stats_; }
@ -126,6 +135,7 @@ class AsanThread {
AsanThreadLocalMallocStorage malloc_storage_;
AsanStats stats_;
bool unwinding_;
bool in_deadly_signal_;
};
// ScopedUnwinding is a scope for stacktracing member of a context
@ -140,6 +150,20 @@ class ScopedUnwinding {
AsanThread *thread;
};
// ScopedDeadlySignal is a scope for handling deadly signals.
class ScopedDeadlySignal {
public:
explicit ScopedDeadlySignal(AsanThread *t) : thread(t) {
if (thread) thread->setInDeadlySignal(true);
}
~ScopedDeadlySignal() {
if (thread) thread->setInDeadlySignal(false);
}
private:
AsanThread *thread;
};
struct CreateThreadContextArgs {
AsanThread *thread;
StackTrace *stack;

View File

@ -53,13 +53,39 @@ extern "C" {
// Otherwise returns 0.
int __asan_address_is_poisoned(void const volatile *addr);
// If at least on byte in [beg, beg+size) is poisoned, return the address
// If at least one byte in [beg, beg+size) is poisoned, return the address
// of the first such byte. Otherwise return 0.
void *__asan_region_is_poisoned(void *beg, size_t size);
// Print the description of addr (useful when debugging in gdb).
void __asan_describe_address(void *addr);
// Useful for calling from a debugger to get information about an ASan error.
// Returns 1 if an error has been (or is being) reported, otherwise returns 0.
int __asan_report_present();
// Useful for calling from a debugger to get information about an ASan error.
// If an error has been (or is being) reported, the following functions return
// the pc, bp, sp, address, access type (0 = read, 1 = write), access size and
// bug description (e.g. "heap-use-after-free"). Otherwise they return 0.
void *__asan_get_report_pc();
void *__asan_get_report_bp();
void *__asan_get_report_sp();
void *__asan_get_report_address();
int __asan_get_report_access_type();
size_t __asan_get_report_access_size();
const char *__asan_get_report_description();
// Useful for calling from the debugger to get information about a pointer.
// Returns the category of the given pointer as a constant string.
// Possible return values are "global", "stack", "stack-fake", "heap",
// "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown".
// If global or stack, tries to also return the variable name, address and
// size. If heap, tries to return the chunk address and size. 'name' should
// point to an allocated buffer of size 'name_size'.
const char *__asan_locate_address(void *addr, char *name, size_t name_size,
void **region_address, size_t *region_size);
// Useful for calling from the debugger to get the allocation stack trace
// and thread ID for a heap address. Stores up to 'size' frames into 'trace',
// returns the number of stored frames or 0 on error.

View File

@ -103,7 +103,7 @@ extern "C" {
const void *end,
const void *old_mid,
const void *new_mid);
// Returns true if the contiguous container [beg, end) ir properly poisoned
// Returns true if the contiguous container [beg, end) is properly poisoned
// (e.g. with __sanitizer_annotate_contiguous_container), i.e. if
// - [beg, mid) is addressable,
// - [mid, end) is unaddressable.

View File

@ -6,6 +6,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
AM_CXXFLAGS += -std=c++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libinterception.la

View File

@ -225,7 +225,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=c++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libinterception.la
interception_files = \

View File

@ -180,7 +180,10 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func) {
}
static const void **InterestingDLLsAvailable() {
const char *InterestingDLLs[] = { "kernel32.dll", "msvcr120.dll", NULL };
const char *InterestingDLLs[] = {"kernel32.dll",
"msvcr110.dll", // VS2012
"msvcr120.dll", // VS2013
NULL};
static void *result[ARRAY_SIZE(InterestingDLLs)] = { 0 };
if (!result[0]) {
for (size_t i = 0, j = 0; InterestingDLLs[i]; ++i) {

View File

@ -40,6 +40,7 @@ C_WARN_FLAGS = $(WARN_FLAGS) -Wstrict-prototypes -Wmissing-prototypes -Wold-styl
CXX_WARN_FLAGS = $(WARN_FLAGS) -Wno-unused-parameter
AM_CFLAGS = $(C_WARN_FLAGS)
AM_CXXFLAGS = $(CXX_WARN_FLAGS) -fno-rtti -fno-exceptions
AM_CXXFLAGS += -std=c++11
noinst_LTLIBRARIES = libsanitizer_libbacktrace.la

View File

@ -270,7 +270,7 @@ WARN_FLAGS = -W -Wall -Wwrite-strings -Wmissing-format-attribute \
C_WARN_FLAGS = $(WARN_FLAGS) -Wstrict-prototypes -Wmissing-prototypes -Wold-style-definition
CXX_WARN_FLAGS = $(WARN_FLAGS) -Wno-unused-parameter
AM_CFLAGS = $(C_WARN_FLAGS)
AM_CXXFLAGS = $(CXX_WARN_FLAGS) -fno-rtti -fno-exceptions
AM_CXXFLAGS = $(CXX_WARN_FLAGS) -fno-rtti -fno-exceptions -std=c++11
noinst_LTLIBRARIES = libsanitizer_libbacktrace.la
libsanitizer_libbacktrace_la_SOURCES = \
../../libbacktrace/backtrace.h \

View File

@ -6,6 +6,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
AM_CXXFLAGS += -std=c++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_lsan.la

View File

@ -260,7 +260,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=c++11
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_lsan.la
@LSAN_SUPPORTED_TRUE@toolexeclib_LTLIBRARIES = liblsan.la

View File

@ -13,17 +13,17 @@
#include "sanitizer_common/sanitizer_flags.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#define GET_STACK_TRACE(max_size, fast) \
StackTrace stack; \
{ \
uptr stack_top = 0, stack_bottom = 0; \
ThreadContext *t; \
if (fast && (t = CurrentThreadContext())) { \
stack_top = t->stack_end(); \
stack_bottom = t->stack_begin(); \
} \
stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
/* context */ 0, stack_top, stack_bottom, fast); \
#define GET_STACK_TRACE(max_size, fast) \
BufferedStackTrace stack; \
{ \
uptr stack_top = 0, stack_bottom = 0; \
ThreadContext *t; \
if (fast && (t = CurrentThreadContext())) { \
stack_top = t->stack_end(); \
stack_bottom = t->stack_begin(); \
} \
stack.Unwind(max_size, StackTrace::GetCurrentPc(), GET_CURRENT_FRAME(), \
/* context */ 0, stack_top, stack_bottom, fast); \
}
#define GET_STACK_TRACE_FATAL \

View File

@ -61,7 +61,7 @@ static void RegisterAllocation(const StackTrace &stack, void *p, uptr size) {
ChunkMetadata *m = Metadata(p);
CHECK(m);
m->tag = DisabledInThisThread() ? kIgnored : kDirectlyLeaked;
m->stack_trace_id = StackDepotPut(stack.trace, stack.size);
m->stack_trace_id = StackDepotPut(stack);
m->requested_size = size;
atomic_store(reinterpret_cast<atomic_uint8_t *>(m), 1, memory_order_relaxed);
}

View File

@ -353,9 +353,7 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) {
static void PrintStackTraceById(u32 stack_trace_id) {
CHECK(stack_trace_id);
uptr size = 0;
const uptr *trace = StackDepotGet(stack_trace_id, &size);
StackTrace::PrintStack(trace, size);
StackDepotGet(stack_trace_id).Print();
}
// ForEachChunk callback. Aggregates information about unreachable chunks into
@ -370,10 +368,9 @@ static void CollectLeaksCb(uptr chunk, void *arg) {
uptr resolution = flags()->resolution;
u32 stack_trace_id = 0;
if (resolution > 0) {
uptr size = 0;
const uptr *trace = StackDepotGet(m.stack_trace_id(), &size);
size = Min(size, resolution);
stack_trace_id = StackDepotPut(trace, size);
StackTrace stack = StackDepotGet(m.stack_trace_id());
stack.size = Min(stack.size, resolution);
stack_trace_id = StackDepotPut(stack);
} else {
stack_trace_id = m.stack_trace_id();
}
@ -449,8 +446,11 @@ void DoLeakCheck() {
PrintMatchedSuppressions();
if (unsuppressed_count > 0) {
param.leak_report.PrintSummary();
if (flags()->exitcode)
if (flags()->exitcode) {
if (common_flags()->coverage)
__sanitizer_cov_dump();
internal__exit(flags()->exitcode);
}
}
}
@ -482,11 +482,10 @@ static Suppression *GetSuppressionForAddr(uptr addr) {
}
static Suppression *GetSuppressionForStack(u32 stack_trace_id) {
uptr size = 0;
const uptr *trace = StackDepotGet(stack_trace_id, &size);
for (uptr i = 0; i < size; i++) {
Suppression *s =
GetSuppressionForAddr(StackTrace::GetPreviousInstructionPc(trace[i]));
StackTrace stack = StackDepotGet(stack_trace_id);
for (uptr i = 0; i < stack.size; i++) {
Suppression *s = GetSuppressionForAddr(
StackTrace::GetPreviousInstructionPc(stack.trace[i]));
if (s) return s;
}
return 0;

View File

@ -92,11 +92,10 @@ void ProcessGlobalRegions(Frontier *frontier) {
static uptr GetCallerPC(u32 stack_id, StackDepotReverseMap *map) {
CHECK(stack_id);
uptr size = 0;
const uptr *trace = map->Get(stack_id, &size);
StackTrace stack = map->Get(stack_id);
// The top frame is our malloc/calloc/etc. The next frame is the caller.
if (size >= 2)
return trace[1];
if (stack.size >= 2)
return stack.trace[1];
return 0;
}

View File

@ -6,6 +6,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
AM_CXXFLAGS += -std=c++11
if LIBBACKTRACE_SUPPORTED
AM_CXXFLAGS += -DSANITIZER_LIBBACKTRACE -DSANITIZER_CP_DEMANGLE \
-I $(top_srcdir)/../libbacktrace \
@ -44,6 +45,7 @@ sanitizer_common_files = \
sanitizer_stackdepot.cc \
sanitizer_stacktrace.cc \
sanitizer_stacktrace_libcdep.cc \
sanitizer_stacktrace_printer.cc \
sanitizer_stoptheworld_linux_libcdep.cc \
sanitizer_suppressions.cc \
sanitizer_symbolizer.cc \

View File

@ -78,6 +78,7 @@ am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \
sanitizer_procmaps_linux.lo sanitizer_procmaps_mac.lo \
sanitizer_stackdepot.lo sanitizer_stacktrace.lo \
sanitizer_stacktrace_libcdep.lo \
sanitizer_stacktrace_printer.lo \
sanitizer_stoptheworld_linux_libcdep.lo \
sanitizer_suppressions.lo sanitizer_symbolizer.lo \
sanitizer_symbolizer_libbacktrace.lo \
@ -252,7 +253,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=c++11 \
$(am__append_1)
ACLOCAL_AMFLAGS = -I m4
noinst_LTLIBRARIES = libsanitizer_common.la
@ -283,6 +284,7 @@ sanitizer_common_files = \
sanitizer_stackdepot.cc \
sanitizer_stacktrace.cc \
sanitizer_stacktrace_libcdep.cc \
sanitizer_stacktrace_printer.cc \
sanitizer_stoptheworld_linux_libcdep.cc \
sanitizer_suppressions.cc \
sanitizer_symbolizer.cc \
@ -414,6 +416,7 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stackdepot.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stacktrace_printer.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_stoptheworld_linux_libcdep.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_suppressions.Plo@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer.Plo@am__quote@

View File

@ -459,6 +459,11 @@ class SizeClassAllocator64 {
}
}
static uptr AdditionalSize() {
return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
GetPageSizeCached());
}
typedef SizeClassMap SizeClassMapT;
static const uptr kNumClasses = SizeClassMap::kNumClasses;
static const uptr kNumClassesRounded = SizeClassMap::kNumClassesRounded;
@ -488,11 +493,6 @@ class SizeClassAllocator64 {
};
COMPILER_CHECK(sizeof(RegionInfo) >= kCacheLineSize);
static uptr AdditionalSize() {
return RoundUpTo(sizeof(RegionInfo) * kNumClassesRounded,
GetPageSizeCached());
}
RegionInfo *GetRegionInfo(uptr class_id) {
CHECK_LT(class_id, kNumClasses);
RegionInfo *regions = reinterpret_cast<RegionInfo*>(kSpaceBeg + kSpaceSize);
@ -1013,12 +1013,15 @@ class LargeMmapAllocator {
if (map_size < size) return AllocatorReturnNull(); // Overflow.
uptr map_beg = reinterpret_cast<uptr>(
MmapOrDie(map_size, "LargeMmapAllocator"));
CHECK(IsAligned(map_beg, page_size_));
MapUnmapCallback().OnMap(map_beg, map_size);
uptr map_end = map_beg + map_size;
uptr res = map_beg + page_size_;
if (res & (alignment - 1)) // Align.
res += alignment - (res & (alignment - 1));
CHECK_EQ(0, res & (alignment - 1));
CHECK(IsAligned(res, alignment));
CHECK(IsAligned(res, page_size_));
CHECK_GE(res + size, map_beg);
CHECK_LE(res + size, map_end);
Header *h = GetHeader(res);
h->size = size;

View File

@ -153,23 +153,12 @@ const char *StripPathPrefix(const char *filepath,
return pos;
}
void PrintSourceLocation(InternalScopedString *buffer, const char *file,
int line, int column) {
CHECK(file);
buffer->append("%s",
StripPathPrefix(file, common_flags()->strip_path_prefix));
if (line > 0) {
buffer->append(":%d", line);
if (column > 0)
buffer->append(":%d", column);
}
}
void PrintModuleAndOffset(InternalScopedString *buffer, const char *module,
uptr offset) {
buffer->append("(%s+0x%zx)",
StripPathPrefix(module, common_flags()->strip_path_prefix),
offset);
const char *StripModuleName(const char *module) {
if (module == 0)
return 0;
if (const char *slash_pos = internal_strrchr(module, '/'))
return slash_pos + 1;
return module;
}
void ReportErrorSummary(const char *error_message) {
@ -215,17 +204,6 @@ bool LoadedModule::containsAddress(uptr address) const {
return false;
}
char *StripModuleName(const char *module) {
if (module == 0)
return 0;
const char *short_module_name = internal_strrchr(module, '/');
if (short_module_name)
short_module_name += 1;
else
short_module_name = module;
return internal_strdup(short_module_name);
}
static atomic_uintptr_t g_total_mmaped;
void IncreaseTotalMmap(uptr size) {

View File

@ -170,10 +170,8 @@ bool IsAccessibleMemoryRange(uptr beg, uptr size);
// Error report formatting.
const char *StripPathPrefix(const char *filepath,
const char *strip_file_prefix);
void PrintSourceLocation(InternalScopedString *buffer, const char *file,
int line, int column);
void PrintModuleAndOffset(InternalScopedString *buffer,
const char *module, uptr offset);
// Strip the directories from the module name.
const char *StripModuleName(const char *module);
// OS
void DisableCoreDumperIfNecessary();
@ -207,9 +205,6 @@ void SleepForMillis(int millis);
u64 NanoTime();
int Atexit(void (*function)(void));
void SortArray(uptr *array, uptr size);
// Strip the directories from the module name, return a new string allocated
// with internal_strdup.
char *StripModuleName(const char *module);
// Exit
void NORETURN Abort();

View File

@ -39,6 +39,10 @@
#define va_copy(dst, src) ((dst) = (src))
#endif // _WIN32
#if SANITIZER_FREEBSD
#define pthread_setname_np pthread_set_name_np
#endif
#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE
#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(p, size) {}
#endif

View File

@ -36,6 +36,7 @@
#include "sanitizer_mutex.h"
#include "sanitizer_procmaps.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_symbolizer.h"
#include "sanitizer_flags.h"
atomic_uint32_t dump_once_guard; // Ensure that CovDump runs only once.
@ -61,6 +62,9 @@ class CoverageData {
void AfterFork(int child_pid);
void Extend(uptr npcs);
void Add(uptr pc);
void IndirCall(uptr caller, uptr callee, uptr callee_cache[],
uptr cache_size);
void DumpCallerCalleePairs();
uptr *data();
uptr size();
@ -83,6 +87,14 @@ class CoverageData {
uptr pc_array_mapped_size;
// Descriptor of the file mapped pc array.
int pc_fd;
// Caller-Callee (cc) array, size and current index.
static const uptr kCcArrayMaxSize = FIRST_32_SECOND_64(1 << 18, 1 << 24);
uptr **cc_array;
atomic_uintptr_t cc_array_index;
atomic_uintptr_t cc_array_size;
StaticSpinMutex mu;
void DirectOpen();
@ -116,6 +128,11 @@ void CoverageData::Init() {
atomic_store(&pc_array_size, kPcArrayMaxSize, memory_order_relaxed);
atomic_store(&pc_array_index, 0, memory_order_relaxed);
}
cc_array = reinterpret_cast<uptr **>(MmapNoReserveOrDie(
sizeof(uptr *) * kCcArrayMaxSize, "CovInit::cc_array"));
atomic_store(&cc_array_size, kCcArrayMaxSize, memory_order_relaxed);
atomic_store(&cc_array_index, 0, memory_order_relaxed);
}
void CoverageData::ReInit() {
@ -184,6 +201,38 @@ void CoverageData::Add(uptr pc) {
pc_array[idx] = pc;
}
// Registers a pair caller=>callee.
// When a given caller is seen for the first time, the callee_cache is added
// to the global array cc_array, callee_cache[0] is set to caller and
// callee_cache[1] is set to cache_size.
// Then we are trying to add callee to callee_cache [2,cache_size) if it is
// not there yet.
// If the cache is full we drop the callee (may want to fix this later).
void CoverageData::IndirCall(uptr caller, uptr callee, uptr callee_cache[],
uptr cache_size) {
if (!cc_array) return;
atomic_uintptr_t *atomic_callee_cache =
reinterpret_cast<atomic_uintptr_t *>(callee_cache);
uptr zero = 0;
if (atomic_compare_exchange_strong(&atomic_callee_cache[0], &zero, caller,
memory_order_seq_cst)) {
uptr idx = atomic_fetch_add(&cc_array_index, 1, memory_order_relaxed);
CHECK_LT(idx * sizeof(uptr),
atomic_load(&cc_array_size, memory_order_acquire));
callee_cache[1] = cache_size;
cc_array[idx] = callee_cache;
}
CHECK_EQ(atomic_load(&atomic_callee_cache[0], memory_order_relaxed), caller);
for (uptr i = 2; i < cache_size; i++) {
uptr was = 0;
if (atomic_compare_exchange_strong(&atomic_callee_cache[i], &was, callee,
memory_order_seq_cst))
return;
if (was == callee) // Already have this callee.
return;
}
}
uptr *CoverageData::data() {
return pc_array;
}
@ -266,6 +315,45 @@ static int CovOpenFile(bool packed, const char* name) {
return fd;
}
// This function dumps the caller=>callee pairs into a file as a sequence of
// lines like "module_name offset".
void CoverageData::DumpCallerCalleePairs() {
uptr max_idx = atomic_load(&cc_array_index, memory_order_relaxed);
if (!max_idx) return;
auto sym = Symbolizer::GetOrInit();
if (!sym)
return;
InternalScopedString out(32 << 20);
uptr total = 0;
for (uptr i = 0; i < max_idx; i++) {
uptr *cc_cache = cc_array[i];
CHECK(cc_cache);
uptr caller = cc_cache[0];
uptr n_callees = cc_cache[1];
const char *caller_module_name = "<unknown>";
uptr caller_module_address = 0;
sym->GetModuleNameAndOffsetForPC(caller, &caller_module_name,
&caller_module_address);
for (uptr j = 2; j < n_callees; j++) {
uptr callee = cc_cache[j];
if (!callee) break;
total++;
const char *callee_module_name = "<unknown>";
uptr callee_module_address = 0;
sym->GetModuleNameAndOffsetForPC(callee, &callee_module_name,
&callee_module_address);
out.append("%s 0x%zx\n%s 0x%zx\n", caller_module_name,
caller_module_address, callee_module_name,
callee_module_address);
}
}
int fd = CovOpenFile(false, "caller-callee");
if (fd < 0) return;
internal_write(fd, out.data(), out.length());
internal_close(fd);
VReport(1, " CovDump: %zd caller-callee pairs written\n", total);
}
// Dump the coverage on disk.
static void CovDump() {
if (!common_flags()->coverage || common_flags()->coverage_direct) return;
@ -297,7 +385,7 @@ static void CovDump() {
CHECK_LE(diff, 0xffffffffU);
offsets.push_back(static_cast<u32>(diff));
}
char *module_name = StripModuleName(module.data());
const char *module_name = StripModuleName(module.data());
if (cov_sandboxed) {
if (cov_fd >= 0) {
CovWritePacked(internal_getpid(), module_name, offsets.data(),
@ -317,11 +405,11 @@ static void CovDump() {
vb - old_vb);
}
}
InternalFree(module_name);
}
}
if (cov_fd >= 0)
internal_close(cov_fd);
coverage_data.DumpCallerCalleePairs();
#endif // !SANITIZER_WINDOWS
}
@ -357,6 +445,11 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov() {
coverage_data.Add(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()));
}
SANITIZER_INTERFACE_ATTRIBUTE void
__sanitizer_cov_indir_call16(uptr callee, uptr callee_cache16[]) {
coverage_data.IndirCall(StackTrace::GetPreviousInstructionPc(GET_CALLER_PC()),
callee, callee_cache16, 16);
}
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); }
SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_init() {
coverage_data.Init();

View File

@ -78,7 +78,7 @@ void CovUpdateMapping(uptr caller_pc) {
text.append("%d\n", sizeof(uptr) * 8);
for (int i = 0; i < n_modules; ++i) {
char *module_name = StripModuleName(modules[i].full_name());
const char *module_name = StripModuleName(modules[i].full_name());
for (unsigned j = 0; j < modules[i].n_ranges(); ++j) {
if (modules[i].address_range_executable(j)) {
uptr start = modules[i].address_range_start(j);
@ -89,7 +89,6 @@ void CovUpdateMapping(uptr caller_pc) {
cached_mapping.SetModuleRange(start, end);
}
}
InternalFree(module_name);
}
int err;

View File

@ -37,6 +37,7 @@ void SetCommonFlagsDefaults(CommonFlags *f) {
f->external_symbolizer_path = 0;
f->allow_addr2line = false;
f->strip_path_prefix = "";
f->fast_unwind_on_check = false;
f->fast_unwind_on_fatal = false;
f->fast_unwind_on_malloc = true;
f->handle_ioctl = false;
@ -64,6 +65,8 @@ void SetCommonFlagsDefaults(CommonFlags *f) {
f->suppressions = "";
f->print_suppressions = true;
f->disable_coredump = (SANITIZER_WORDSIZE == 64);
f->symbolize_inline_frames = true;
f->stack_trace_format = "DEFAULT";
}
void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
@ -79,6 +82,9 @@ void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
"unavailable.");
ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix",
"Strips this prefix from file paths in error reports.");
ParseFlag(str, &f->fast_unwind_on_check, "fast_unwind_on_check",
"If available, use the fast frame-pointer-based unwinder on "
"internal CHECK failures.");
ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal",
"If available, use the fast frame-pointer-based unwinder on fatal "
"errors.");
@ -152,6 +158,12 @@ void ParseCommonFlagsFromString(CommonFlags *f, const char *str) {
"Disable core dumping. By default, disable_core=1 on 64-bit to avoid "
"dumping a 16T+ core file. Ignored on OSes that don't dump core by"
"default and for sanitizers that don't reserve lots of virtual memory.");
ParseFlag(str, &f->symbolize_inline_frames, "symbolize_inline_frames",
"Print inlined frames in stacktraces. Defaults to true.");
ParseFlag(str, &f->stack_trace_format, "stack_trace_format",
"Format string used to render stack frames. "
"See sanitizer_stacktrace_printer.h for the format description. "
"Use DEFAULT to get default format.");
// Do a sanity check for certain flags.
if (f->malloc_context_size < 1)

View File

@ -30,6 +30,7 @@ struct CommonFlags {
const char *external_symbolizer_path;
bool allow_addr2line;
const char *strip_path_prefix;
bool fast_unwind_on_check;
bool fast_unwind_on_fatal;
bool fast_unwind_on_malloc;
bool handle_ioctl;
@ -58,6 +59,8 @@ struct CommonFlags {
const char *suppressions;
bool print_suppressions;
bool disable_coredump;
bool symbolize_inline_frames;
const char *stack_trace_format;
};
inline CommonFlags *common_flags() {

View File

@ -158,7 +158,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top,
// pthread_get_stacksize_np() returns an incorrect stack size for the main
// thread on Mavericks. See
// https://code.google.com/p/address-sanitizer/issues/detail?id=261
if ((GetMacosVersion() == MACOS_VERSION_MAVERICKS) && at_initialization &&
if ((GetMacosVersion() >= MACOS_VERSION_MAVERICKS) && at_initialization &&
stacksize == (1 << 19)) {
struct rlimit rl;
CHECK_EQ(getrlimit(RLIMIT_STACK, &rl), 0);
@ -295,6 +295,7 @@ MacosVersion GetMacosVersionInternal() {
case '1': return MACOS_VERSION_LION;
case '2': return MACOS_VERSION_MOUNTAIN_LION;
case '3': return MACOS_VERSION_MAVERICKS;
case '4': return MACOS_VERSION_YOSEMITE;
default: return MACOS_VERSION_UNKNOWN;
}
}

View File

@ -23,7 +23,8 @@ enum MacosVersion {
MACOS_VERSION_SNOW_LEOPARD,
MACOS_VERSION_LION,
MACOS_VERSION_MOUNTAIN_LION,
MACOS_VERSION_MAVERICKS
MACOS_VERSION_MAVERICKS,
MACOS_VERSION_YOSEMITE,
};
MacosVersion GetMacosVersion();

View File

@ -47,7 +47,7 @@
# define SANITIZER_WINDOWS 0
#endif
#if defined(__ANDROID__) || defined(ANDROID)
#if defined(__ANDROID__)
# define SANITIZER_ANDROID 1
#else
# define SANITIZER_ANDROID 0
@ -79,7 +79,7 @@
// For such platforms build this code with -DSANITIZER_CAN_USE_ALLOCATOR64=0 or
// change the definition of SANITIZER_CAN_USE_ALLOCATOR64 here.
#ifndef SANITIZER_CAN_USE_ALLOCATOR64
# if defined(__aarch64__)
# if defined(__aarch64__) || defined(__mips64)
# define SANITIZER_CAN_USE_ALLOCATOR64 0
# else
# define SANITIZER_CAN_USE_ALLOCATOR64 (SANITIZER_WORDSIZE == 64)
@ -107,4 +107,10 @@
# endif
#endif
#ifdef __mips__
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 10)
#else
# define SANITIZER_POINTER_FORMAT_LENGTH FIRST_32_SECOND_64(8, 12)
#endif
#endif // SANITIZER_PLATFORM_H

View File

@ -204,7 +204,8 @@
#define SANITIZER_INTERCEPT__EXIT SI_LINUX || SI_FREEBSD
#define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP \
SI_FREEBSD || SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_TLS_GET_ADDR SI_LINUX_NOT_ANDROID
@ -226,7 +227,8 @@
#define SANITIZER_INTERCEPT_OBSTACK SI_LINUX_NOT_ANDROID
#define SANITIZER_INTERCEPT_FFLUSH SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_FCLOSE SI_NOT_WINDOWS
#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_DLOPEN_DLCLOSE \
SI_FREEBSD || SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_GETPASS SI_LINUX_NOT_ANDROID || SI_MAC
#define SANITIZER_INTERCEPT_TIMERFD SI_LINUX_NOT_ANDROID

View File

@ -36,7 +36,6 @@
#define uid_t __kernel_uid_t
#define gid_t __kernel_gid_t
#define off_t __kernel_off_t
#define time_t __kernel_time_t
// This header seems to contain the definitions of _kernel_ stat* structs.
#include <asm/stat.h>
#undef ino_t
@ -61,7 +60,7 @@ namespace __sanitizer {
} // namespace __sanitizer
#if !defined(__powerpc64__) && !defined(__x86_64__) && !defined(__aarch64__)\
&& !defined(__mips__) && !defined(__sparc__)
&& !defined(__mips__)
COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat));
#endif

View File

@ -13,7 +13,19 @@
#include "sanitizer_platform.h"
#if SANITIZER_LINUX || SANITIZER_FREEBSD || SANITIZER_MAC
// Tests in this file assume that off_t-dependent data structures match the
// libc ABI. For example, struct dirent here is what readdir() function (as
// exported from libc) returns, and not the user-facing "dirent", which
// depends on _FILE_OFFSET_BITS setting.
// To get this "true" dirent definition, we undefine _FILE_OFFSET_BITS below.
#ifdef _FILE_OFFSET_BITS
#undef _FILE_OFFSET_BITS
#endif
#if SANITIZER_FREEBSD
#define _WANT_RTENTRY
#include <sys/param.h>
#include <sys/socketvar.h>
#endif
#include <arpa/inet.h>
#include <dirent.h>
#include <errno.h>
@ -551,7 +563,9 @@ namespace __sanitizer {
unsigned IOCTL_PPPIOCSMAXCID = PPPIOCSMAXCID;
unsigned IOCTL_PPPIOCSMRU = PPPIOCSMRU;
unsigned IOCTL_PPPIOCSXASYNCMAP = PPPIOCSXASYNCMAP;
unsigned IOCTL_SIOCADDRT = SIOCADDRT;
unsigned IOCTL_SIOCDARP = SIOCDARP;
unsigned IOCTL_SIOCDELRT = SIOCDELRT;
unsigned IOCTL_SIOCDRARP = SIOCDRARP;
unsigned IOCTL_SIOCGARP = SIOCGARP;
unsigned IOCTL_SIOCGIFENCAP = SIOCGIFENCAP;
@ -637,8 +651,6 @@ namespace __sanitizer {
#if SANITIZER_LINUX || SANITIZER_FREEBSD
unsigned IOCTL_MTIOCGET = MTIOCGET;
unsigned IOCTL_MTIOCTOP = MTIOCTOP;
unsigned IOCTL_SIOCADDRT = SIOCADDRT;
unsigned IOCTL_SIOCDELRT = SIOCDELRT;
unsigned IOCTL_SNDCTL_DSP_GETBLKSIZE = SNDCTL_DSP_GETBLKSIZE;
unsigned IOCTL_SNDCTL_DSP_GETFMTS = SNDCTL_DSP_GETFMTS;
unsigned IOCTL_SNDCTL_DSP_NONBLOCK = SNDCTL_DSP_NONBLOCK;

View File

@ -72,14 +72,6 @@ namespace __sanitizer {
const unsigned struct_kernel_stat_sz = 144;
#endif
const unsigned struct_kernel_stat64_sz = 104;
#elif defined(__sparc__) && defined(__arch64__)
const unsigned struct___old_kernel_stat_sz = 0;
const unsigned struct_kernel_stat_sz = 104;
const unsigned struct_kernel_stat64_sz = 144;
#elif defined(__sparc__) && !defined(__arch64__)
const unsigned struct___old_kernel_stat_sz = 0;
const unsigned struct_kernel_stat_sz = 64;
const unsigned struct_kernel_stat64_sz = 104;
#endif
struct __sanitizer_perf_event_attr {
unsigned type;
@ -102,7 +94,7 @@ namespace __sanitizer {
#if defined(__powerpc64__)
const unsigned struct___old_kernel_stat_sz = 0;
#elif !defined(__sparc__)
#else
const unsigned struct___old_kernel_stat_sz = 32;
#endif
@ -181,18 +173,6 @@ namespace __sanitizer {
unsigned short __pad1;
unsigned long __unused1;
unsigned long __unused2;
#elif defined(__sparc__)
# if defined(__arch64__)
unsigned mode;
unsigned short __pad1;
# else
unsigned short __pad1;
unsigned short mode;
unsigned short __pad2;
# endif
unsigned short __seq;
unsigned long long __unused1;
unsigned long long __unused2;
#else
unsigned short mode;
unsigned short __pad1;
@ -210,26 +190,6 @@ namespace __sanitizer {
struct __sanitizer_shmid_ds {
__sanitizer_ipc_perm shm_perm;
#if defined(__sparc__)
# if !defined(__arch64__)
u32 __pad1;
# endif
long shm_atime;
# if !defined(__arch64__)
u32 __pad2;
# endif
long shm_dtime;
# if !defined(__arch64__)
u32 __pad3;
# endif
long shm_ctime;
uptr shm_segsz;
int shm_cpid;
int shm_lpid;
unsigned long shm_nattch;
unsigned long __glibc_reserved1;
unsigned long __glibc_reserved2;
#else
#ifndef __powerpc__
uptr shm_segsz;
#elif !defined(__powerpc64__)
@ -267,7 +227,6 @@ namespace __sanitizer {
uptr __unused4;
uptr __unused5;
#endif
#endif
};
#elif SANITIZER_FREEBSD
struct __sanitizer_ipc_perm {
@ -511,7 +470,7 @@ namespace __sanitizer {
typedef long __sanitizer___kernel_off_t;
#endif
#if defined(__powerpc__) || defined(__aarch64__) || defined(__mips__)
#if defined(__powerpc__) || defined(__mips__)
typedef unsigned int __sanitizer___kernel_old_uid_t;
typedef unsigned int __sanitizer___kernel_old_gid_t;
#else
@ -564,13 +523,9 @@ namespace __sanitizer {
#else
__sanitizer_sigset_t sa_mask;
#ifndef __mips__
#if defined(__sparc__)
unsigned long sa_flags;
#else
int sa_flags;
#endif
#endif
#endif
#if SANITIZER_LINUX
void (*sa_restorer)();
#endif
@ -790,7 +745,7 @@ struct __sanitizer_obstack {
#define IOC_NRBITS 8
#define IOC_TYPEBITS 8
#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__) || defined(__sparc__)
#if defined(__powerpc__) || defined(__powerpc64__) || defined(__mips__)
#define IOC_SIZEBITS 13
#define IOC_DIRBITS 3
#define IOC_NONE 1U
@ -820,17 +775,7 @@ struct __sanitizer_obstack {
#define IOC_DIR(nr) (((nr) >> IOC_DIRSHIFT) & IOC_DIRMASK)
#define IOC_TYPE(nr) (((nr) >> IOC_TYPESHIFT) & IOC_TYPEMASK)
#define IOC_NR(nr) (((nr) >> IOC_NRSHIFT) & IOC_NRMASK)
#if defined(__sparc__)
// In sparc the 14 bits SIZE field overlaps with the
// least significant bit of DIR, so either IOC_READ or
// IOC_WRITE shall be 1 in order to get a non-zero SIZE.
# define IOC_SIZE(nr) \
((((((nr) >> 29) & 0x7) & (4U|2U)) == 0)? \
0 : (((nr) >> 16) & 0x3fff))
#else
#define IOC_SIZE(nr) (((nr) >> IOC_SIZESHIFT) & IOC_SIZEMASK)
#endif
extern unsigned struct_arpreq_sz;
extern unsigned struct_ifreq_sz;

View File

@ -82,9 +82,12 @@ uptr GetMaxVirtualAddress() {
// one of 0x00000fffffffffffUL and 0x00003fffffffffffUL.
// Note that with 'ulimit -s unlimited' the stack is moved away from the top
// of the address space, so simply checking the stack address is not enough.
return (1ULL << 44) - 1; // 0x00000fffffffffffUL
// This should (does) work for both PowerPC64 Endian modes.
return (1ULL << (MostSignificantSetBitIndex(GET_CURRENT_FRAME()) + 1)) - 1;
# elif defined(__aarch64__)
return (1ULL << 39) - 1;
# elif defined(__mips64)
return (1ULL << 40) - 1;
# else
return (1ULL << 47) - 1; // 0x00007fffffffffffUL;
# endif

View File

@ -111,7 +111,7 @@ static int AppendPointer(char **buff, const char *buff_end, u64 ptr_value) {
int result = 0;
result += AppendString(buff, buff_end, -1, "0x");
result += AppendUnsigned(buff, buff_end, ptr_value, 16,
(SANITIZER_WORDSIZE == 64) ? 12 : 8, true);
SANITIZER_POINTER_FORMAT_LENGTH, true);
return result;
}

View File

@ -16,31 +16,6 @@
namespace __sanitizer {
struct StackDepotDesc {
const uptr *stack;
uptr size;
u32 hash() const {
// murmur2
const u32 m = 0x5bd1e995;
const u32 seed = 0x9747b28c;
const u32 r = 24;
u32 h = seed ^ (size * sizeof(uptr));
for (uptr i = 0; i < size; i++) {
u32 k = stack[i];
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
}
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
bool is_valid() { return size > 0 && stack; }
};
struct StackDepotNode {
StackDepotNode *link;
u32 id;
@ -56,28 +31,49 @@ struct StackDepotNode {
static const u32 kUseCountMask = (1 << kUseCountBits) - 1;
static const u32 kHashMask = ~kUseCountMask;
typedef StackDepotDesc args_type;
typedef StackTrace args_type;
bool eq(u32 hash, const args_type &args) const {
u32 hash_bits =
atomic_load(&hash_and_use_count, memory_order_relaxed) & kHashMask;
if ((hash & kHashMask) != hash_bits || args.size != size) return false;
uptr i = 0;
for (; i < size; i++) {
if (stack[i] != args.stack[i]) return false;
if (stack[i] != args.trace[i]) return false;
}
return true;
}
static uptr storage_size(const args_type &args) {
return sizeof(StackDepotNode) + (args.size - 1) * sizeof(uptr);
}
static u32 hash(const args_type &args) {
// murmur2
const u32 m = 0x5bd1e995;
const u32 seed = 0x9747b28c;
const u32 r = 24;
u32 h = seed ^ (args.size * sizeof(uptr));
for (uptr i = 0; i < args.size; i++) {
u32 k = args.trace[i];
k *= m;
k ^= k >> r;
k *= m;
h *= m;
h ^= k;
}
h ^= h >> 13;
h *= m;
h ^= h >> 15;
return h;
}
static bool is_valid(const args_type &args) {
return args.size > 0 && args.trace;
}
void store(const args_type &args, u32 hash) {
atomic_store(&hash_and_use_count, hash & kHashMask, memory_order_relaxed);
size = args.size;
internal_memcpy(stack, args.stack, size * sizeof(uptr));
internal_memcpy(stack, args.trace, size * sizeof(uptr));
}
args_type load() const {
args_type ret = {&stack[0], size};
return ret;
return args_type(&stack[0], size);
}
StackDepotHandle get_handle() { return StackDepotHandle(this); }
@ -97,8 +93,6 @@ void StackDepotHandle::inc_use_count_unsafe() {
StackDepotNode::kUseCountMask;
CHECK_LT(prev + 1, StackDepotNode::kMaxUseCount);
}
uptr StackDepotHandle::size() { return node_->size; }
uptr *StackDepotHandle::stack() { return &node_->stack[0]; }
// FIXME(dvyukov): this single reserved bit is used in TSan.
typedef StackDepotBase<StackDepotNode, 1, StackDepotNode::kTabSizeLog>
@ -109,21 +103,17 @@ StackDepotStats *StackDepotGetStats() {
return theDepot.GetStats();
}
u32 StackDepotPut(const uptr *stack, uptr size) {
StackDepotDesc desc = {stack, size};
StackDepotHandle h = theDepot.Put(desc);
u32 StackDepotPut(StackTrace stack) {
StackDepotHandle h = theDepot.Put(stack);
return h.valid() ? h.id() : 0;
}
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size) {
StackDepotDesc desc = {stack, size};
return theDepot.Put(desc);
StackDepotHandle StackDepotPut_WithHandle(StackTrace stack) {
return theDepot.Put(stack);
}
const uptr *StackDepotGet(u32 id, uptr *size) {
StackDepotDesc desc = theDepot.Get(id);
*size = desc.size;
return desc.stack;
StackTrace StackDepotGet(u32 id) {
return theDepot.Get(id);
}
void StackDepotLockAll() {
@ -154,18 +144,15 @@ StackDepotReverseMap::StackDepotReverseMap()
InternalSort(&map_, map_.size(), IdDescPair::IdComparator);
}
const uptr *StackDepotReverseMap::Get(u32 id, uptr *size) {
if (!map_.size()) return 0;
StackTrace StackDepotReverseMap::Get(u32 id) {
if (!map_.size())
return StackTrace();
IdDescPair pair = {id, 0};
uptr idx = InternalBinarySearch(map_, 0, map_.size(), pair,
IdDescPair::IdComparator);
if (idx > map_.size()) {
*size = 0;
return 0;
}
StackDepotNode *desc = map_[idx].desc;
*size = desc->size;
return desc->stack;
if (idx > map_.size())
return StackTrace();
return map_[idx].desc->load();
}
} // namespace __sanitizer

View File

@ -13,6 +13,7 @@
#include "sanitizer_common.h"
#include "sanitizer_internal_defs.h"
#include "sanitizer_stacktrace.h"
namespace __sanitizer {
@ -26,17 +27,15 @@ struct StackDepotHandle {
u32 id();
int use_count();
void inc_use_count_unsafe();
uptr size();
uptr *stack();
};
const int kStackDepotMaxUseCount = 1U << 20;
StackDepotStats *StackDepotGetStats();
u32 StackDepotPut(const uptr *stack, uptr size);
StackDepotHandle StackDepotPut_WithHandle(const uptr *stack, uptr size);
u32 StackDepotPut(StackTrace stack);
StackDepotHandle StackDepotPut_WithHandle(StackTrace stack);
// Retrieves a stored stack trace by the id.
const uptr *StackDepotGet(u32 id, uptr *size);
StackTrace StackDepotGet(u32 id);
void StackDepotLockAll();
void StackDepotUnlockAll();
@ -48,7 +47,7 @@ void StackDepotUnlockAll();
class StackDepotReverseMap {
public:
StackDepotReverseMap();
const uptr *Get(u32 id, uptr *size);
StackTrace Get(u32 id);
private:
struct IdDescPair {

View File

@ -95,8 +95,8 @@ typename StackDepotBase<Node, kReservedBits, kTabSizeLog>::handle_type
StackDepotBase<Node, kReservedBits, kTabSizeLog>::Put(args_type args,
bool *inserted) {
if (inserted) *inserted = false;
if (!args.is_valid()) return handle_type();
uptr h = args.hash();
if (!Node::is_valid(args)) return handle_type();
uptr h = Node::hash(args);
atomic_uintptr_t *p = &tab[h % kTabSize];
uptr v = atomic_load(p, memory_order_consume);
Node *s = (Node *)(v & ~1);

View File

@ -23,7 +23,7 @@ uptr StackTrace::GetPreviousInstructionPc(uptr pc) {
#if defined(__powerpc__) || defined(__powerpc64__)
// PCs are always 4 byte aligned.
return pc - 4;
#elif defined(__sparc__)
#elif defined(__sparc__) || defined(__mips__)
return pc - 8;
#else
return pc - 1;
@ -34,6 +34,15 @@ uptr StackTrace::GetCurrentPc() {
return GET_CALLER_PC();
}
void BufferedStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
size = cnt + !!extra_top_pc;
CHECK_LE(size, kStackTraceMax);
internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
if (extra_top_pc)
trace_buffer[cnt] = extra_top_pc;
top_frame_bp = 0;
}
// Check if given pointer points into allocated stack area.
static inline bool IsValidFrame(uptr frame, uptr stack_top, uptr stack_bottom) {
return frame > stack_bottom && frame < stack_top - 2 * sizeof (uhwptr);
@ -49,32 +58,40 @@ static inline uhwptr *GetCanonicFrame(uptr bp,
if (!IsValidFrame(bp, stack_top, stack_bottom)) return 0;
uhwptr *bp_prev = (uhwptr *)bp;
if (IsValidFrame((uptr)bp_prev[0], stack_top, stack_bottom)) return bp_prev;
return bp_prev - 1;
// The next frame pointer does not look right. This could be a GCC frame, step
// back by 1 word and try again.
if (IsValidFrame((uptr)bp_prev[-1], stack_top, stack_bottom))
return bp_prev - 1;
// Nope, this does not look right either. This means the frame after next does
// not have a valid frame pointer, but we can still extract the caller PC.
// Unfortunately, there is no way to decide between GCC and LLVM frame
// layouts. Assume LLVM.
return bp_prev;
#else
return (uhwptr*)bp;
#endif
}
void StackTrace::FastUnwindStack(uptr pc, uptr bp,
uptr stack_top, uptr stack_bottom,
uptr max_depth) {
void BufferedStackTrace::FastUnwindStack(uptr pc, uptr bp, uptr stack_top,
uptr stack_bottom, uptr max_depth) {
CHECK_GE(max_depth, 2);
trace[0] = pc;
trace_buffer[0] = pc;
size = 1;
if (stack_top < 4096) return; // Sanity check for stack top.
uhwptr *frame = GetCanonicFrame(bp, stack_top, stack_bottom);
uhwptr *prev_frame = 0;
// Lowest possible address that makes sense as the next frame pointer.
// Goes up as we walk the stack.
uptr bottom = stack_bottom;
// Avoid infinite loop when frame == frame[0] by using frame > prev_frame.
while (frame > prev_frame &&
IsValidFrame((uptr)frame, stack_top, stack_bottom) &&
while (IsValidFrame((uptr)frame, stack_top, bottom) &&
IsAligned((uptr)frame, sizeof(*frame)) &&
size < max_depth) {
uhwptr pc1 = frame[1];
if (pc1 != pc) {
trace[size++] = (uptr) pc1;
trace_buffer[size++] = (uptr) pc1;
}
prev_frame = frame;
frame = GetCanonicFrame((uptr)frame[0], stack_top, stack_bottom);
bottom = (uptr)frame;
frame = GetCanonicFrame((uptr)frame[0], stack_top, bottom);
}
}
@ -82,15 +99,15 @@ static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) {
return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold;
}
void StackTrace::PopStackFrames(uptr count) {
void BufferedStackTrace::PopStackFrames(uptr count) {
CHECK_LT(count, size);
size -= count;
for (uptr i = 0; i < size; ++i) {
trace[i] = trace[i + count];
trace_buffer[i] = trace_buffer[i + count];
}
}
uptr StackTrace::LocatePcInTrace(uptr pc) {
uptr BufferedStackTrace::LocatePcInTrace(uptr pc) {
// Use threshold to find PC in stack trace, as PC we want to unwind from may
// slightly differ from return address in the actual unwinded stack trace.
const int kPcThreshold = 288;

View File

@ -27,44 +27,49 @@ static const uptr kStackTraceMax = 256;
# define SANITIZER_CAN_FAST_UNWIND 1
#endif
// Fast unwind is the only option on Mac for now; we will need to
// revisit this macro when slow unwind works on Mac, see
// https://code.google.com/p/address-sanitizer/issues/detail?id=137
#if SANITIZER_MAC
# define SANITIZER_CAN_SLOW_UNWIND 0
#else
# define SANITIZER_CAN_SLOW_UNWIND 1
#endif
struct StackTrace {
typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
int out_size);
uptr top_frame_bp;
const uptr *trace;
uptr size;
uptr trace[kStackTraceMax];
StackTrace() : trace(nullptr), size(0) {}
StackTrace(const uptr *trace, uptr size) : trace(trace), size(size) {}
// Prints a symbolized stacktrace, followed by an empty line.
static void PrintStack(const uptr *addr, uptr size);
void Print() const {
PrintStack(trace, size);
}
void CopyFrom(const uptr *src, uptr src_size) {
top_frame_bp = 0;
size = src_size;
if (size > kStackTraceMax) size = kStackTraceMax;
for (uptr i = 0; i < size; i++)
trace[i] = src[i];
}
void Print() const;
static bool WillUseFastUnwind(bool request_fast_unwind) {
// Check if fast unwind is available. Fast unwind is the only option on Mac.
// It is also the only option on FreeBSD as the slow unwinding that
// leverages _Unwind_Backtrace() yields the call stack of the signal's
// handler and not of the code that raised the signal (as it does on Linux).
if (!SANITIZER_CAN_FAST_UNWIND)
return false;
else if (SANITIZER_MAC != 0 || SANITIZER_FREEBSD != 0)
else if (!SANITIZER_CAN_SLOW_UNWIND)
return true;
return request_fast_unwind;
}
void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
uptr stack_bottom, bool request_fast_unwind);
static uptr GetCurrentPc();
static uptr GetPreviousInstructionPc(uptr pc);
typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer,
int out_size);
};
// StackTrace that owns the buffer used to store the addresses.
struct BufferedStackTrace : public StackTrace {
uptr trace_buffer[kStackTraceMax];
uptr top_frame_bp; // Optional bp of a top frame.
BufferedStackTrace() : StackTrace(trace_buffer, 0), top_frame_bp(0) {}
void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
void Unwind(uptr max_depth, uptr pc, uptr bp, void *context, uptr stack_top,
uptr stack_bottom, bool request_fast_unwind);
private:
void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom,
@ -74,6 +79,9 @@ struct StackTrace {
uptr max_depth);
void PopStackFrames(uptr count);
uptr LocatePcInTrace(uptr pc);
BufferedStackTrace(const BufferedStackTrace &);
void operator=(const BufferedStackTrace &);
};
} // namespace __sanitizer

View File

@ -10,58 +10,40 @@
//===----------------------------------------------------------------------===//
#include "sanitizer_common.h"
#include "sanitizer_placement_new.h"
#include "sanitizer_stacktrace.h"
#include "sanitizer_stacktrace_printer.h"
#include "sanitizer_symbolizer.h"
namespace __sanitizer {
static void PrintStackFramePrefix(InternalScopedString *buffer, uptr frame_num,
uptr pc) {
buffer->append(" #%zu 0x%zx", frame_num, pc);
}
void StackTrace::PrintStack(const uptr *addr, uptr size) {
if (addr == 0 || size == 0) {
void StackTrace::Print() const {
if (trace == nullptr || size == 0) {
Printf(" <empty stack>\n\n");
return;
}
InternalScopedBuffer<char> buff(GetPageSizeCached() * 2);
InternalScopedBuffer<AddressInfo> addr_frames(64);
const int kMaxAddrFrames = 64;
InternalScopedBuffer<AddressInfo> addr_frames(kMaxAddrFrames);
for (uptr i = 0; i < kMaxAddrFrames; i++)
new(&addr_frames[i]) AddressInfo();
InternalScopedString frame_desc(GetPageSizeCached() * 2);
uptr frame_num = 0;
for (uptr i = 0; i < size && addr[i]; i++) {
for (uptr i = 0; i < size && trace[i]; i++) {
// PCs in stack traces are actually the return addresses, that is,
// addresses of the next instructions after the call.
uptr pc = GetPreviousInstructionPc(addr[i]);
uptr pc = GetPreviousInstructionPc(trace[i]);
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
pc, addr_frames.data(), addr_frames.size());
pc, addr_frames.data(), kMaxAddrFrames);
if (addr_frames_num == 0) {
frame_desc.clear();
PrintStackFramePrefix(&frame_desc, frame_num, pc);
frame_desc.append(" (<unknown module>)");
Printf("%s\n", frame_desc.data());
frame_num++;
continue;
addr_frames[0].address = pc;
addr_frames_num = 1;
}
for (uptr j = 0; j < addr_frames_num; j++) {
AddressInfo &info = addr_frames[j];
frame_desc.clear();
PrintStackFramePrefix(&frame_desc, frame_num, pc);
if (info.function) {
frame_desc.append(" in %s", info.function);
// Print offset in function if we don't know the source file.
if (!info.file && info.function_offset != AddressInfo::kUnknown)
frame_desc.append("+0x%zx", info.function_offset);
}
if (info.file) {
frame_desc.append(" ");
PrintSourceLocation(&frame_desc, info.file, info.line, info.column);
} else if (info.module) {
frame_desc.append(" ");
PrintModuleAndOffset(&frame_desc, info.module, info.module_offset);
}
RenderFrame(&frame_desc, common_flags()->stack_trace_format, frame_num++,
info, common_flags()->strip_path_prefix);
Printf("%s\n", frame_desc.data());
frame_num++;
info.Clear();
}
}
@ -69,9 +51,9 @@ void StackTrace::PrintStack(const uptr *addr, uptr size) {
Printf("\n");
}
void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
uptr stack_top, uptr stack_bottom,
bool request_fast_unwind) {
void BufferedStackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
uptr stack_top, uptr stack_bottom,
bool request_fast_unwind) {
top_frame_bp = (max_depth > 0) ? bp : 0;
// Avoid doing any work for small max_depth.
if (max_depth == 0) {
@ -80,7 +62,7 @@ void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, void *context,
}
if (max_depth == 1) {
size = 1;
trace[0] = pc;
trace_buffer[0] = pc;
return;
}
if (!WillUseFastUnwind(request_fast_unwind)) {

View File

@ -0,0 +1,130 @@
//===-- sanitizer_common.cc -----------------------------------------------===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between sanitizers' run-time libraries.
//
//===----------------------------------------------------------------------===//
#include "sanitizer_stacktrace_printer.h"
namespace __sanitizer {
static const char *StripFunctionName(const char *function, const char *prefix) {
if (function == 0) return 0;
if (prefix == 0) return function;
uptr prefix_len = internal_strlen(prefix);
if (0 == internal_strncmp(function, prefix, prefix_len))
return function + prefix_len;
return function;
}
static const char kDefaultFormat[] = " #%n %p %F %L";
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
const AddressInfo &info, const char *strip_path_prefix,
const char *strip_func_prefix) {
if (0 == internal_strcmp(format, "DEFAULT"))
format = kDefaultFormat;
for (const char *p = format; *p != '\0'; p++) {
if (*p != '%') {
buffer->append("%c", *p);
continue;
}
p++;
switch (*p) {
case '%':
buffer->append("%%");
break;
// Frame number and all fields of AddressInfo structure.
case 'n':
buffer->append("%zu", frame_no);
break;
case 'p':
buffer->append("0x%zx", info.address);
break;
case 'm':
buffer->append("%s", StripPathPrefix(info.module, strip_path_prefix));
break;
case 'o':
buffer->append("0x%zx", info.module_offset);
break;
case 'f':
buffer->append("%s", StripFunctionName(info.function, strip_func_prefix));
break;
case 'q':
buffer->append("0x%zx", info.function_offset != AddressInfo::kUnknown
? info.function_offset
: 0x0);
break;
case 's':
buffer->append("%s", StripPathPrefix(info.file, strip_path_prefix));
break;
case 'l':
buffer->append("%d", info.line);
break;
case 'c':
buffer->append("%d", info.column);
break;
// Smarter special cases.
case 'F':
// Function name and offset, if file is unknown.
if (info.function) {
buffer->append("in %s",
StripFunctionName(info.function, strip_func_prefix));
if (!info.file && info.function_offset != AddressInfo::kUnknown)
buffer->append("+0x%zx", info.function_offset);
}
break;
case 'S':
// File/line information.
RenderSourceLocation(buffer, info.file, info.line, info.column,
strip_path_prefix);
break;
case 'L':
// Source location, or module location.
if (info.file) {
RenderSourceLocation(buffer, info.file, info.line, info.column,
strip_path_prefix);
} else if (info.module) {
RenderModuleLocation(buffer, info.module, info.module_offset,
strip_path_prefix);
} else {
buffer->append("(<unknown module>)");
}
break;
case 'M':
// Module basename and offset, or PC.
if (info.module)
buffer->append("(%s+%p)", StripModuleName(info.module),
(void *)info.module_offset);
else
buffer->append("(%p)", (void *)info.address);
break;
default:
Report("Unsupported specifier in stack frame format: %c (0x%zx)!\n",
*p, *p);
Die();
}
}
}
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
int line, int column, const char *strip_path_prefix) {
buffer->append("%s", StripPathPrefix(file, strip_path_prefix));
if (line > 0) {
buffer->append(":%d", line);
if (column > 0)
buffer->append(":%d", column);
}
}
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
uptr offset, const char *strip_path_prefix) {
buffer->append("(%s+0x%zx)", StripPathPrefix(module, strip_path_prefix),
offset);
}
} // namespace __sanitizer

View File

@ -0,0 +1,60 @@
//===-- sanitizer_stacktrace_printer.h --------------------------*- C++ -*-===//
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between sanitizers' run-time libraries.
//
//===----------------------------------------------------------------------===//
#ifndef SANITIZER_STACKTRACE_PRINTER_H
#define SANITIZER_STACKTRACE_PRINTER_H
#include "sanitizer_common.h"
#include "sanitizer_symbolizer.h"
namespace __sanitizer {
// Render the contents of "info" structure, which represents the contents of
// stack frame "frame_no" and appends it to the "buffer". "format" is a
// string with placeholders, which is copied to the output with
// placeholders substituted with the contents of "info". For example,
// format string
// " frame %n: function %F at %S"
// will be turned into
// " frame 10: function foo::bar() at my/file.cc:10"
// You may additionally pass "strip_path_prefix" to strip prefixes of paths to
// source files and modules, and "strip_func_prefix" to strip prefixes of
// function names.
// Here's the full list of available placeholders:
// %% - represents a '%' character;
// %n - frame number (copy of frame_no);
// %p - PC in hex format;
// %m - path to module (binary or shared object);
// %o - offset in the module in hex format;
// %f - function name;
// %q - offset in the function in hex format (*if available*);
// %s - path to source file;
// %l - line in the source file;
// %c - column in the source file;
// %F - if function is known to be <foo>, prints "in <foo>", possibly
// followed by the offset in this function, but only if source file
// is unknown;
// %S - prints file/line/column information;
// %L - prints location information: file/line/column, if it is known, or
// module+offset if it is known, or (<unknown module>) string.
// %M - prints module basename and offset, if it is known, or PC.
void RenderFrame(InternalScopedString *buffer, const char *format, int frame_no,
const AddressInfo &info, const char *strip_path_prefix = "",
const char *strip_func_prefix = "");
void RenderSourceLocation(InternalScopedString *buffer, const char *file,
int line, int column, const char *strip_path_prefix);
void RenderModuleLocation(InternalScopedString *buffer, const char *module,
uptr offset, const char *strip_path_prefix);
} // namespace __sanitizer
#endif // SANITIZER_STACKTRACE_PRINTER_H

View File

@ -59,13 +59,23 @@ struct AddressInfo {
}
};
// For now, DataInfo is used to describe global variable.
struct DataInfo {
uptr address;
char *module;
uptr module_offset;
char *name;
uptr start;
uptr size;
DataInfo() {
internal_memset(this, 0, sizeof(DataInfo));
}
void Clear() {
InternalFree(module);
InternalFree(name);
internal_memset(this, 0, sizeof(DataInfo));
}
};
class Symbolizer {

View File

@ -166,7 +166,7 @@ uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames,
}
bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) {
backtrace_syminfo((backtrace_state *)state_, info->address,
backtrace_syminfo((backtrace_state *)state_, info->start,
SymbolizeDataCallback, ErrorCallback, info);
return true;
}

View File

@ -339,12 +339,19 @@ class LLVMSymbolizerProcess : public SymbolizerProcess {
const char* const kSymbolizerArch = "--default-arch=x86_64";
#elif defined(__i386__)
const char* const kSymbolizerArch = "--default-arch=i386";
#elif defined(__powerpc64__)
#elif defined(__powerpc64__) && defined(__BIG_ENDIAN__)
const char* const kSymbolizerArch = "--default-arch=powerpc64";
#elif defined(__powerpc64__) && defined(__LITTLE_ENDIAN__)
const char* const kSymbolizerArch = "--default-arch=powerpc64le";
#else
const char* const kSymbolizerArch = "--default-arch=unknown";
#endif
execl(path_to_binary, path_to_binary, kSymbolizerArch, (char *)0);
const char *const inline_flag = common_flags()->symbolize_inline_frames
? "--inlining=true"
: "--inlining=false";
execl(path_to_binary, path_to_binary, inline_flag, kSymbolizerArch,
(char *)0);
}
};
@ -580,8 +587,7 @@ class POSIXSymbolizer : public Symbolizer {
return false;
const char *module_name = module->full_name();
uptr module_offset = addr - module->base_address();
internal_memset(info, 0, sizeof(*info));
info->address = addr;
info->Clear();
info->module = internal_strdup(module_name);
info->module_offset = module_offset;
// First, try to use libbacktrace symbolizer (if it's available).

View File

@ -93,7 +93,7 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) {
}
struct UnwindTraceArg {
StackTrace *stack;
BufferedStackTrace *stack;
uptr max_depth;
};
@ -101,27 +101,27 @@ _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) {
UnwindTraceArg *arg = (UnwindTraceArg*)param;
CHECK_LT(arg->stack->size, arg->max_depth);
uptr pc = Unwind_GetIP(ctx);
arg->stack->trace[arg->stack->size++] = pc;
arg->stack->trace_buffer[arg->stack->size++] = pc;
if (arg->stack->size == arg->max_depth) return UNWIND_STOP;
return UNWIND_CONTINUE;
}
void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
CHECK_GE(max_depth, 2);
size = 0;
UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)};
_Unwind_Backtrace(Unwind_Trace, &arg);
// We need to pop a few frames so that pc is on top.
uptr to_pop = LocatePcInTrace(pc);
// trace[0] belongs to the current function so we always pop it.
if (to_pop == 0)
// trace_buffer[0] belongs to the current function so we always pop it.
if (to_pop == 0 && size > 1)
to_pop = 1;
PopStackFrames(to_pop);
trace[0] = pc;
trace_buffer[0] = pc;
}
void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth) {
void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth) {
CHECK_GE(max_depth, 2);
if (!unwind_backtrace_signal_arch) {
SlowUnwindStack(pc, max_depth);
@ -143,7 +143,7 @@ void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
// +2 compensate for libcorkscrew unwinder returning addresses of call
// instructions instead of raw return addresses.
for (sptr i = 0; i < res; ++i)
trace[size++] = frames[i].absolute_pc + 2;
trace_buffer[size++] = frames[i].absolute_pc + 2;
}
} // namespace __sanitizer

View File

@ -442,7 +442,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
}
#if !SANITIZER_GO
void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
void BufferedStackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
CHECK_GE(max_depth, 2);
// FIXME: CaptureStackBackTrace might be too slow for us.
// FIXME: Compare with StackWalk64.
@ -457,8 +457,8 @@ void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) {
PopStackFrames(pc_location);
}
void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth) {
void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
uptr max_depth) {
CONTEXT ctx = *(CONTEXT *)context;
STACKFRAME64 stack_frame;
memset(&stack_frame, 0, sizeof(stack_frame));
@ -481,7 +481,7 @@ void StackTrace::SlowUnwindStackWithContext(uptr pc, void *context,
&stack_frame, &ctx, NULL, &SymFunctionTableAccess64,
&SymGetModuleBase64, NULL) &&
size < Min(max_depth, kStackTraceMax)) {
trace[size++] = (uptr)stack_frame.AddrPC.Offset;
trace_buffer[size++] = (uptr)stack_frame.AddrPC.Offset;
}
}
#endif // #if !SANITIZER_GO

View File

@ -6,6 +6,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
AM_CXXFLAGS += -std=c++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libtsan.la

View File

@ -276,7 +276,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=c++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libtsan.la
tsan_files = \

View File

@ -41,7 +41,6 @@ const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit.
const int kClkBits = 42;
const unsigned kMaxTidReuse = (1 << (64 - kClkBits)) - 1;
const uptr kShadowStackSize = 64 * 1024;
const uptr kTraceStackSize = 256;
#ifdef TSAN_SHADOW_COUNT
# if TSAN_SHADOW_COUNT == 2 \
@ -172,7 +171,6 @@ struct Context;
struct ReportStack;
class ReportDesc;
class RegionAlloc;
class StackTrace;
// Descriptor of user's memory block.
struct MBlock {

View File

@ -46,7 +46,8 @@ static bool bogusfd(int fd) {
}
static FdSync *allocsync(ThreadState *thr, uptr pc) {
FdSync *s = (FdSync*)user_alloc(thr, pc, sizeof(FdSync));
FdSync *s = (FdSync*)user_alloc(thr, pc, sizeof(FdSync), kDefaultAlignment,
false);
atomic_store(&s->rc, 1, memory_order_relaxed);
return s;
}
@ -63,7 +64,7 @@ static void unref(ThreadState *thr, uptr pc, FdSync *s) {
CHECK_NE(s, &fdctx.globsync);
CHECK_NE(s, &fdctx.filesync);
CHECK_NE(s, &fdctx.socksync);
user_free(thr, pc, s);
user_free(thr, pc, s, false);
}
}
}
@ -76,13 +77,13 @@ static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) {
if (l1 == 0) {
uptr size = kTableSizeL2 * sizeof(FdDesc);
// We need this to reside in user memory to properly catch races on it.
void *p = user_alloc(thr, pc, size);
void *p = user_alloc(thr, pc, size, kDefaultAlignment, false);
internal_memset(p, 0, size);
MemoryResetRange(thr, (uptr)&fddesc, (uptr)p, size);
if (atomic_compare_exchange_strong(pl1, &l1, (uptr)p, memory_order_acq_rel))
l1 = (uptr)p;
else
user_free(thr, pc, p);
user_free(thr, pc, p, false);
}
return &((FdDesc*)l1)[fd % kTableSizeL2]; // NOLINT
}

View File

@ -97,6 +97,7 @@ void InitializeFlags(Flags *f, const char *env) {
cf->allow_addr2line = true;
cf->detect_deadlocks = true;
cf->print_suppressions = false;
cf->stack_trace_format = " #%n %f %S %M";
// Let a frontend override.
ParseFlags(f, __tsan_default_options());

View File

@ -27,6 +27,16 @@
using namespace __tsan; // NOLINT
#if SANITIZER_FREEBSD
#define __errno_location __error
#define __libc_malloc __malloc
#define __libc_realloc __realloc
#define __libc_calloc __calloc
#define __libc_free __free
#define stdout __stdoutp
#define stderr __stderrp
#endif
const int kSigCount = 65;
struct my_siginfo_t {
@ -60,7 +70,9 @@ extern "C" void *__libc_malloc(uptr size);
extern "C" void *__libc_calloc(uptr size, uptr n);
extern "C" void *__libc_realloc(void *ptr, uptr size);
extern "C" void __libc_free(void *ptr);
#if !SANITIZER_FREEBSD
extern "C" int mallopt(int param, int value);
#endif
extern __sanitizer_FILE *stdout, *stderr;
const int PTHREAD_MUTEX_RECURSIVE = 1;
const int PTHREAD_MUTEX_RECURSIVE_NP = 1;
@ -91,19 +103,19 @@ typedef void (*sighandler_t)(int sig);
#define errno (*__errno_location())
// 16K loaded modules should be enough for everyone.
static const uptr kMaxModules = 1 << 14;
static LoadedModule *modules;
static uptr nmodules;
struct sigaction_t {
union {
sighandler_t sa_handler;
void (*sa_sigaction)(int sig, my_siginfo_t *siginfo, void *uctx);
};
#if SANITIZER_FREEBSD
int sa_flags;
__sanitizer_sigset_t sa_mask;
#else
__sanitizer_sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)();
#endif
};
const sighandler_t SIG_DFL = (sighandler_t)0;
@ -202,7 +214,7 @@ ScopedInterceptor::~ScopedInterceptor() {
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
ScopedInterceptor si(thr, #func, caller_pc); \
const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
/**/
@ -218,7 +230,11 @@ ScopedInterceptor::~ScopedInterceptor() {
#define TSAN_INTERCEPTOR(ret, func, ...) INTERCEPTOR(ret, func, __VA_ARGS__)
#define TSAN_INTERCEPT(func) INTERCEPT_FUNCTION(func)
#define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
#if SANITIZER_FREEBSD
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION(func)
#else
# define TSAN_INTERCEPT_VER(func, ver) INTERCEPT_FUNCTION_VER(func, ver)
#endif
#define BLOCK_REAL(name) (BlockingCall(thr), REAL(name))
@ -271,63 +287,24 @@ TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) {
return res;
}
class AtExitContext {
public:
AtExitContext()
: mtx_(MutexTypeAtExit, StatMtxAtExit)
, stack_(MBlockAtExit) {
}
typedef void(*atexit_cb_t)();
int atexit(ThreadState *thr, uptr pc, bool is_on_exit,
atexit_cb_t f, void *arg, void *dso) {
Lock l(&mtx_);
Release(thr, pc, (uptr)this);
atexit_t *a = stack_.PushBack();
a->cb = f;
a->arg = arg;
a->dso = dso;
a->is_on_exit = is_on_exit;
return 0;
}
void exit(ThreadState *thr, uptr pc) {
for (;;) {
atexit_t a = {};
{
Lock l(&mtx_);
if (stack_.Size() != 0) {
a = stack_[stack_.Size() - 1];
stack_.PopBack();
Acquire(thr, pc, (uptr)this);
}
}
if (a.cb == 0)
break;
VPrintf(2, "#%d: executing atexit func %p(%p) dso=%p\n",
thr->tid, a.cb, a.arg, a.dso);
if (a.is_on_exit)
((void(*)(int status, void *arg))a.cb)(0, a.arg);
else
((void(*)(void *arg, void *dso))a.cb)(a.arg, a.dso);
}
}
private:
struct atexit_t {
atexit_cb_t cb;
void *arg;
void *dso;
bool is_on_exit;
};
static const int kMaxAtExit = 1024;
Mutex mtx_;
Vector<atexit_t> stack_;
// The sole reason tsan wraps atexit callbacks is to establish synchronization
// between callback setup and callback execution.
struct AtExitCtx {
void (*f)();
void *arg;
};
static AtExitContext *atexit_ctx;
static void at_exit_wrapper(void *arg) {
ThreadState *thr = cur_thread();
uptr pc = 0;
Acquire(thr, pc, (uptr)arg);
AtExitCtx *ctx = (AtExitCtx*)arg;
((void(*)(void *arg))ctx->f)(ctx->arg);
__libc_free(ctx);
}
static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
void *arg, void *dso);
TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
if (cur_thread()->in_symbolizer)
@ -335,40 +312,51 @@ TSAN_INTERCEPTOR(int, atexit, void (*f)()) {
// We want to setup the atexit callback even if we are in ignored lib
// or after fork.
SCOPED_INTERCEPTOR_RAW(atexit, f);
return atexit_ctx->atexit(thr, pc, false, (void(*)())f, 0, 0);
}
TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
if (cur_thread()->in_symbolizer)
return 0;
SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
return atexit_ctx->atexit(thr, pc, true, (void(*)())f, arg, 0);
}
bool IsSaticModule(void *dso) {
if (modules == 0)
return false;
for (uptr i = 0; i < nmodules; i++) {
if (modules[i].containsAddress((uptr)dso))
return true;
}
return false;
return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0);
}
TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) {
if (cur_thread()->in_symbolizer)
return 0;
SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso);
// If it's the main executable or a statically loaded library,
// we will call the callback.
if (dso == 0 || IsSaticModule(dso))
return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg, dso);
return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso);
}
// Dynamically load module, don't know when to call the callback for it.
static int setup_at_exit_wrapper(ThreadState *thr, uptr pc, void(*f)(),
void *arg, void *dso) {
AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx));
ctx->f = f;
ctx->arg = arg;
Release(thr, pc, (uptr)ctx);
// Memory allocation in __cxa_atexit will race with free during exit,
// because we do not see synchronization around atexit callback list.
ThreadIgnoreBegin(thr, pc);
int res = REAL(__cxa_atexit)(f, arg, dso);
int res = REAL(__cxa_atexit)(at_exit_wrapper, ctx, dso);
ThreadIgnoreEnd(thr, pc);
return res;
}
static void on_exit_wrapper(int status, void *arg) {
ThreadState *thr = cur_thread();
uptr pc = 0;
Acquire(thr, pc, (uptr)arg);
AtExitCtx *ctx = (AtExitCtx*)arg;
((void(*)(int status, void *arg))ctx->f)(status, ctx->arg);
__libc_free(ctx);
}
TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) {
if (cur_thread()->in_symbolizer)
return 0;
SCOPED_TSAN_INTERCEPTOR(on_exit, f, arg);
AtExitCtx *ctx = (AtExitCtx*)__libc_malloc(sizeof(AtExitCtx));
ctx->f = (void(*)())f;
ctx->arg = arg;
Release(thr, pc, (uptr)ctx);
// Memory allocation in __cxa_atexit will race with free during exit,
// because we do not see synchronization around atexit callback list.
ThreadIgnoreBegin(thr, pc);
int res = REAL(on_exit)(on_exit_wrapper, ctx);
ThreadIgnoreEnd(thr, pc);
return res;
}
@ -406,7 +394,11 @@ static void SetJmp(ThreadState *thr, uptr sp, uptr mangled_sp) {
}
static void LongJmp(ThreadState *thr, uptr *env) {
#if SANITIZER_FREEBSD
uptr mangled_sp = env[2];
#else
uptr mangled_sp = env[6];
#endif // SANITIZER_FREEBSD
// Find the saved buf by mangled_sp.
for (uptr i = 0; i < thr->jmp_bufs.Size(); i++) {
JmpBuf *buf = &thr->jmp_bufs[i];
@ -751,6 +743,7 @@ TSAN_INTERCEPTOR(void*, mmap, void *addr, long_t sz, int prot,
return res;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
int flags, int fd, u64 off) {
SCOPED_TSAN_INTERCEPTOR(mmap64, addr, sz, prot, flags, fd, off);
@ -764,6 +757,10 @@ TSAN_INTERCEPTOR(void*, mmap64, void *addr, long_t sz, int prot,
}
return res;
}
#define TSAN_MAYBE_INTERCEPT_MMAP64 TSAN_INTERCEPT(mmap64)
#else
#define TSAN_MAYBE_INTERCEPT_MMAP64
#endif
TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
SCOPED_TSAN_INTERCEPTOR(munmap, addr, sz);
@ -772,10 +769,15 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) {
return res;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
return user_alloc(thr, pc, sz, align);
}
#define TSAN_MAYBE_INTERCEPT_MEMALIGN TSAN_INTERCEPT(memalign)
#else
#define TSAN_MAYBE_INTERCEPT_MEMALIGN
#endif
TSAN_INTERCEPTOR(void*, aligned_alloc, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(memalign, align, sz);
@ -787,11 +789,16 @@ TSAN_INTERCEPTOR(void*, valloc, uptr sz) {
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) {
SCOPED_INTERCEPTOR_RAW(pvalloc, sz);
sz = RoundUp(sz, GetPageSizeCached());
return user_alloc(thr, pc, sz, GetPageSizeCached());
}
#define TSAN_MAYBE_INTERCEPT_PVALLOC TSAN_INTERCEPT(pvalloc)
#else
#define TSAN_MAYBE_INTERCEPT_PVALLOC
#endif
TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) {
SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz);
@ -863,11 +870,13 @@ extern "C" void *__tsan_thread_start_func(void *arg) {
ThreadState *thr = cur_thread();
// Thread-local state is not initialized yet.
ScopedIgnoreInterceptors ignore;
ThreadIgnoreBegin(thr, 0);
if (pthread_setspecific(g_thread_finalize_key,
(void *)kPthreadDestructorIterations)) {
Printf("ThreadSanitizer: failed to set thread key\n");
Die();
}
ThreadIgnoreEnd(thr, 0);
while ((tid = atomic_load(&p->tid, memory_order_acquire)) == 0)
pthread_yield();
atomic_store(&p->tid, 0, memory_order_release);
@ -1336,73 +1345,135 @@ TSAN_INTERCEPTOR(int, sem_getvalue, void *s, int *sval) {
return res;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat, version, path, buf);
return REAL(__xstat)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___XSTAT TSAN_INTERCEPT(__xstat)
#else
#define TSAN_MAYBE_INTERCEPT___XSTAT
#endif
TSAN_INTERCEPTOR(int, stat, const char *path, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(stat, path, buf);
return REAL(stat)(path, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__xstat, 0, path, buf);
return REAL(__xstat)(0, path, buf);
#endif
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __xstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, version, path, buf);
return REAL(__xstat64)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___XSTAT64 TSAN_INTERCEPT(__xstat64)
#else
#define TSAN_MAYBE_INTERCEPT___XSTAT64
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, stat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__xstat64, 0, path, buf);
return REAL(__xstat64)(0, path, buf);
}
#define TSAN_MAYBE_INTERCEPT_STAT64 TSAN_INTERCEPT(stat64)
#else
#define TSAN_MAYBE_INTERCEPT_STAT64
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat, version, path, buf);
return REAL(__lxstat)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___LXSTAT TSAN_INTERCEPT(__lxstat)
#else
#define TSAN_MAYBE_INTERCEPT___LXSTAT
#endif
TSAN_INTERCEPTOR(int, lstat, const char *path, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(lstat, path, buf);
return REAL(lstat)(path, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__lxstat, 0, path, buf);
return REAL(__lxstat)(0, path, buf);
#endif
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __lxstat64, int version, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, version, path, buf);
return REAL(__lxstat64)(version, path, buf);
}
#define TSAN_MAYBE_INTERCEPT___LXSTAT64 TSAN_INTERCEPT(__lxstat64)
#else
#define TSAN_MAYBE_INTERCEPT___LXSTAT64
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, lstat64, const char *path, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__lxstat64, 0, path, buf);
return REAL(__lxstat64)(0, path, buf);
}
#define TSAN_MAYBE_INTERCEPT_LSTAT64 TSAN_INTERCEPT(lstat64)
#else
#define TSAN_MAYBE_INTERCEPT_LSTAT64
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __fxstat, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat, version, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat)(version, fd, buf);
}
#define TSAN_MAYBE_INTERCEPT___FXSTAT TSAN_INTERCEPT(__fxstat)
#else
#define TSAN_MAYBE_INTERCEPT___FXSTAT
#endif
TSAN_INTERCEPTOR(int, fstat, int fd, void *buf) {
#if SANITIZER_FREEBSD
SCOPED_TSAN_INTERCEPTOR(fstat, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(fstat)(fd, buf);
#else
SCOPED_TSAN_INTERCEPTOR(__fxstat, 0, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat)(0, fd, buf);
#endif
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __fxstat64, int version, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, version, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat64)(version, fd, buf);
}
#define TSAN_MAYBE_INTERCEPT___FXSTAT64 TSAN_INTERCEPT(__fxstat64)
#else
#define TSAN_MAYBE_INTERCEPT___FXSTAT64
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, fstat64, int fd, void *buf) {
SCOPED_TSAN_INTERCEPTOR(__fxstat64, 0, fd, buf);
if (fd > 0)
FdAccess(thr, pc, fd);
return REAL(__fxstat64)(0, fd, buf);
}
#define TSAN_MAYBE_INTERCEPT_FSTAT64 TSAN_INTERCEPT(fstat64)
#else
#define TSAN_MAYBE_INTERCEPT_FSTAT64
#endif
TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open, name, flags, mode);
@ -1412,6 +1483,7 @@ TSAN_INTERCEPTOR(int, open, const char *name, int flags, int mode) {
return fd;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
SCOPED_TSAN_INTERCEPTOR(open64, name, flags, mode);
int fd = REAL(open64)(name, flags, mode);
@ -1419,6 +1491,10 @@ TSAN_INTERCEPTOR(int, open64, const char *name, int flags, int mode) {
FdFileCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_OPEN64 TSAN_INTERCEPT(open64)
#else
#define TSAN_MAYBE_INTERCEPT_OPEN64
#endif
TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat, name, mode);
@ -1428,6 +1504,7 @@ TSAN_INTERCEPTOR(int, creat, const char *name, int mode) {
return fd;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
SCOPED_TSAN_INTERCEPTOR(creat64, name, mode);
int fd = REAL(creat64)(name, mode);
@ -1435,6 +1512,10 @@ TSAN_INTERCEPTOR(int, creat64, const char *name, int mode) {
FdFileCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_CREAT64 TSAN_INTERCEPT(creat64)
#else
#define TSAN_MAYBE_INTERCEPT_CREAT64
#endif
TSAN_INTERCEPTOR(int, dup, int oldfd) {
SCOPED_TSAN_INTERCEPTOR(dup, oldfd);
@ -1460,6 +1541,7 @@ TSAN_INTERCEPTOR(int, dup3, int oldfd, int newfd, int flags) {
return newfd2;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
SCOPED_TSAN_INTERCEPTOR(eventfd, initval, flags);
int fd = REAL(eventfd)(initval, flags);
@ -1467,7 +1549,12 @@ TSAN_INTERCEPTOR(int, eventfd, unsigned initval, int flags) {
FdEventCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_EVENTFD TSAN_INTERCEPT(eventfd)
#else
#define TSAN_MAYBE_INTERCEPT_EVENTFD
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
SCOPED_TSAN_INTERCEPTOR(signalfd, fd, mask, flags);
if (fd >= 0)
@ -1477,7 +1564,12 @@ TSAN_INTERCEPTOR(int, signalfd, int fd, void *mask, int flags) {
FdSignalCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_SIGNALFD TSAN_INTERCEPT(signalfd)
#else
#define TSAN_MAYBE_INTERCEPT_SIGNALFD
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, inotify_init, int fake) {
SCOPED_TSAN_INTERCEPTOR(inotify_init, fake);
int fd = REAL(inotify_init)(fake);
@ -1485,7 +1577,12 @@ TSAN_INTERCEPTOR(int, inotify_init, int fake) {
FdInotifyCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT TSAN_INTERCEPT(inotify_init)
#else
#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
SCOPED_TSAN_INTERCEPTOR(inotify_init1, flags);
int fd = REAL(inotify_init1)(flags);
@ -1493,6 +1590,10 @@ TSAN_INTERCEPTOR(int, inotify_init1, int flags) {
FdInotifyCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1 TSAN_INTERCEPT(inotify_init1)
#else
#define TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1
#endif
TSAN_INTERCEPTOR(int, socket, int domain, int type, int protocol) {
SCOPED_TSAN_INTERCEPTOR(socket, domain, type, protocol);
@ -1535,6 +1636,7 @@ TSAN_INTERCEPTOR(int, listen, int fd, int backlog) {
return res;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_create, int size) {
SCOPED_TSAN_INTERCEPTOR(epoll_create, size);
int fd = REAL(epoll_create)(size);
@ -1542,7 +1644,12 @@ TSAN_INTERCEPTOR(int, epoll_create, int size) {
FdPollCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE TSAN_INTERCEPT(epoll_create)
#else
#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
SCOPED_TSAN_INTERCEPTOR(epoll_create1, flags);
int fd = REAL(epoll_create1)(flags);
@ -1550,6 +1657,10 @@ TSAN_INTERCEPTOR(int, epoll_create1, int flags) {
FdPollCreate(thr, pc, fd);
return fd;
}
#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1 TSAN_INTERCEPT(epoll_create1)
#else
#define TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1
#endif
TSAN_INTERCEPTOR(int, close, int fd) {
SCOPED_TSAN_INTERCEPTOR(close, fd);
@ -1558,14 +1669,20 @@ TSAN_INTERCEPTOR(int, close, int fd) {
return REAL(close)(fd);
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, __close, int fd) {
SCOPED_TSAN_INTERCEPTOR(__close, fd);
if (fd >= 0)
FdClose(thr, pc, fd);
return REAL(__close)(fd);
}
#define TSAN_MAYBE_INTERCEPT___CLOSE TSAN_INTERCEPT(__close)
#else
#define TSAN_MAYBE_INTERCEPT___CLOSE
#endif
// glibc guts
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
SCOPED_TSAN_INTERCEPTOR(__res_iclose, state, free_addr);
int fds[64];
@ -1576,6 +1693,10 @@ TSAN_INTERCEPTOR(void, __res_iclose, void *state, bool free_addr) {
}
REAL(__res_iclose)(state, free_addr);
}
#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE TSAN_INTERCEPT(__res_iclose)
#else
#define TSAN_MAYBE_INTERCEPT___RES_ICLOSE
#endif
TSAN_INTERCEPTOR(int, pipe, int *pipefd) {
SCOPED_TSAN_INTERCEPTOR(pipe, pipefd);
@ -1642,6 +1763,7 @@ TSAN_INTERCEPTOR(void*, tmpfile, int fake) {
return res;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
SCOPED_TSAN_INTERCEPTOR(tmpfile64, fake);
void *res = REAL(tmpfile64)(fake);
@ -1652,6 +1774,10 @@ TSAN_INTERCEPTOR(void*, tmpfile64, int fake) {
}
return res;
}
#define TSAN_MAYBE_INTERCEPT_TMPFILE64 TSAN_INTERCEPT(tmpfile64)
#else
#define TSAN_MAYBE_INTERCEPT_TMPFILE64
#endif
TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) {
// libc file streams can call user-supplied functions, see fopencookie.
@ -1698,6 +1824,7 @@ TSAN_INTERCEPTOR(void*, opendir, char *path) {
return res;
}
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev);
if (epfd >= 0)
@ -1709,7 +1836,12 @@ TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) {
int res = REAL(epoll_ctl)(epfd, op, fd, ev);
return res;
}
#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL TSAN_INTERCEPT(epoll_ctl)
#else
#define TSAN_MAYBE_INTERCEPT_EPOLL_CTL
#endif
#if !SANITIZER_FREEBSD
TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
SCOPED_TSAN_INTERCEPTOR(epoll_wait, epfd, ev, cnt, timeout);
if (epfd >= 0)
@ -1719,6 +1851,10 @@ TSAN_INTERCEPTOR(int, epoll_wait, int epfd, void *ev, int cnt, int timeout) {
FdAcquire(thr, pc, epfd);
return res;
}
#define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT TSAN_INTERCEPT(epoll_wait)
#else
#define TSAN_MAYBE_INTERCEPT_EPOLL_WAIT
#endif
namespace __tsan {
@ -1746,12 +1882,12 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
// from rtl_generic_sighandler) we have not yet received the reraised
// signal; and it looks too fragile to intercept all ways to reraise a signal.
if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) {
__tsan::StackTrace stack;
stack.ObtainCurrent(thr, pc);
VarSizeStackTrace stack;
ObtainCurrentStack(thr, pc, &stack);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeErrnoInSignal);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack, true);
rep.AddStack(stack, true);
OutputReport(thr, rep);
}
}
@ -2226,8 +2362,6 @@ namespace __tsan {
static void finalize(void *arg) {
ThreadState *thr = cur_thread();
uptr pc = 0;
atexit_ctx->exit(thr, pc);
int status = Finalize(thr);
// Make sure the output is not lost.
// Flushing all the streams here may freeze the process if a child thread is
@ -2250,8 +2384,10 @@ void InitializeInterceptors() {
REAL(memcmp) = internal_memcmp;
// Instruct libc malloc to consume less memory.
#if !SANITIZER_FREEBSD
mallopt(1, 0); // M_MXFAST
mallopt(-3, 32*1024); // M_MMAP_THRESHOLD
#endif
InitializeCommonInterceptors();
@ -2273,11 +2409,11 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(free);
TSAN_INTERCEPT(cfree);
TSAN_INTERCEPT(mmap);
TSAN_INTERCEPT(mmap64);
TSAN_MAYBE_INTERCEPT_MMAP64;
TSAN_INTERCEPT(munmap);
TSAN_INTERCEPT(memalign);
TSAN_MAYBE_INTERCEPT_MEMALIGN;
TSAN_INTERCEPT(valloc);
TSAN_INTERCEPT(pvalloc);
TSAN_MAYBE_INTERCEPT_PVALLOC;
TSAN_INTERCEPT(posix_memalign);
TSAN_INTERCEPT(strlen);
@ -2340,38 +2476,38 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(sem_getvalue);
TSAN_INTERCEPT(stat);
TSAN_INTERCEPT(__xstat);
TSAN_INTERCEPT(stat64);
TSAN_INTERCEPT(__xstat64);
TSAN_MAYBE_INTERCEPT___XSTAT;
TSAN_MAYBE_INTERCEPT_STAT64;
TSAN_MAYBE_INTERCEPT___XSTAT64;
TSAN_INTERCEPT(lstat);
TSAN_INTERCEPT(__lxstat);
TSAN_INTERCEPT(lstat64);
TSAN_INTERCEPT(__lxstat64);
TSAN_MAYBE_INTERCEPT___LXSTAT;
TSAN_MAYBE_INTERCEPT_LSTAT64;
TSAN_MAYBE_INTERCEPT___LXSTAT64;
TSAN_INTERCEPT(fstat);
TSAN_INTERCEPT(__fxstat);
TSAN_INTERCEPT(fstat64);
TSAN_INTERCEPT(__fxstat64);
TSAN_MAYBE_INTERCEPT___FXSTAT;
TSAN_MAYBE_INTERCEPT_FSTAT64;
TSAN_MAYBE_INTERCEPT___FXSTAT64;
TSAN_INTERCEPT(open);
TSAN_INTERCEPT(open64);
TSAN_MAYBE_INTERCEPT_OPEN64;
TSAN_INTERCEPT(creat);
TSAN_INTERCEPT(creat64);
TSAN_MAYBE_INTERCEPT_CREAT64;
TSAN_INTERCEPT(dup);
TSAN_INTERCEPT(dup2);
TSAN_INTERCEPT(dup3);
TSAN_INTERCEPT(eventfd);
TSAN_INTERCEPT(signalfd);
TSAN_INTERCEPT(inotify_init);
TSAN_INTERCEPT(inotify_init1);
TSAN_MAYBE_INTERCEPT_EVENTFD;
TSAN_MAYBE_INTERCEPT_SIGNALFD;
TSAN_MAYBE_INTERCEPT_INOTIFY_INIT;
TSAN_MAYBE_INTERCEPT_INOTIFY_INIT1;
TSAN_INTERCEPT(socket);
TSAN_INTERCEPT(socketpair);
TSAN_INTERCEPT(connect);
TSAN_INTERCEPT(bind);
TSAN_INTERCEPT(listen);
TSAN_INTERCEPT(epoll_create);
TSAN_INTERCEPT(epoll_create1);
TSAN_MAYBE_INTERCEPT_EPOLL_CREATE;
TSAN_MAYBE_INTERCEPT_EPOLL_CREATE1;
TSAN_INTERCEPT(close);
TSAN_INTERCEPT(__close);
TSAN_INTERCEPT(__res_iclose);
TSAN_MAYBE_INTERCEPT___CLOSE;
TSAN_MAYBE_INTERCEPT___RES_ICLOSE;
TSAN_INTERCEPT(pipe);
TSAN_INTERCEPT(pipe2);
@ -2381,7 +2517,7 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(unlink);
TSAN_INTERCEPT(tmpfile);
TSAN_INTERCEPT(tmpfile64);
TSAN_MAYBE_INTERCEPT_TMPFILE64;
TSAN_INTERCEPT(fread);
TSAN_INTERCEPT(fwrite);
TSAN_INTERCEPT(abort);
@ -2389,8 +2525,8 @@ void InitializeInterceptors() {
TSAN_INTERCEPT(rmdir);
TSAN_INTERCEPT(opendir);
TSAN_INTERCEPT(epoll_ctl);
TSAN_INTERCEPT(epoll_wait);
TSAN_MAYBE_INTERCEPT_EPOLL_CTL;
TSAN_MAYBE_INTERCEPT_EPOLL_WAIT;
TSAN_INTERCEPT(sigaction);
TSAN_INTERCEPT(signal);
@ -2413,9 +2549,6 @@ void InitializeInterceptors() {
// Need to setup it, because interceptors check that the function is resolved.
// But atexit is emitted directly into the module, so can't be resolved.
REAL(atexit) = (int(*)(void(*)()))unreachable;
atexit_ctx = new(internal_alloc(MBlockAtExit, sizeof(AtExitContext)))
AtExitContext();
if (REAL(__cxa_atexit)(&finalize, 0, 0)) {
Printf("ThreadSanitizer: failed to setup atexit callback\n");
Die();
@ -2427,11 +2560,6 @@ void InitializeInterceptors() {
}
FdInit();
// Remember list of loaded libraries for atexit interceptors.
modules = (LoadedModule*)MmapOrDie(sizeof(*modules)*kMaxModules,
"LoadedModule");
nmodules = GetListOfModules(modules, kMaxModules, 0);
}
void *internal_start_thread(void(*func)(void *arg), void *arg) {

View File

@ -52,7 +52,7 @@ class ScopedAnnotation {
StatInc(thr, StatAnnotation); \
StatInc(thr, Stat##typ); \
ScopedAnnotation sa(thr, __func__, f, l, caller_pc); \
const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
/**/
@ -452,4 +452,6 @@ const char INTERFACE_ATTRIBUTE* ThreadSanitizerQuery(const char *query) {
void INTERFACE_ATTRIBUTE
AnnotateMemoryIsInitialized(char *f, int l, uptr mem, uptr sz) {}
void INTERFACE_ATTRIBUTE
AnnotateMemoryIsUninitialized(char *f, int l, uptr mem, uptr sz) {}
} // extern "C"

View File

@ -472,7 +472,7 @@ static void AtomicFence(ThreadState *thr, uptr pc, morder mo) {
#define SCOPED_ATOMIC(func, ...) \
const uptr callpc = (uptr)__builtin_return_address(0); \
uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
uptr pc = StackTrace::GetCurrentPc(); \
mo = flags()->force_seq_cst_atomics ? (morder)mo_seq_cst : mo; \
ThreadState *const thr = cur_thread(); \
if (thr->ignore_interceptors) \

View File

@ -59,7 +59,7 @@ static JavaContext *jctx;
#define SCOPED_JAVA_FUNC(func) \
ThreadState *thr = cur_thread(); \
const uptr caller_pc = GET_CALLER_PC(); \
const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \
const uptr pc = StackTrace::GetCurrentPc(); \
(void)pc; \
ScopedJavaFunc scoped(thr, caller_pc); \
/**/

View File

@ -64,17 +64,17 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
if (atomic_load(&thr->in_signal_handler, memory_order_relaxed) == 0 ||
!flags()->report_signal_unsafe)
return;
StackTrace stack;
stack.ObtainCurrent(thr, pc);
VarSizeStackTrace stack;
ObtainCurrentStack(thr, pc, &stack);
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeSignalUnsafe);
if (!IsFiredSuppression(ctx, rep, stack)) {
rep.AddStack(&stack, true);
rep.AddStack(stack, true);
OutputReport(thr, rep);
}
}
void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align, bool signal) {
if ((sz >= (1ull << 40)) || (align >= (1ull << 40)))
return AllocatorReturnNull();
void *p = allocator()->Allocate(&thr->alloc_cache, sz, align);
@ -82,15 +82,17 @@ void *user_alloc(ThreadState *thr, uptr pc, uptr sz, uptr align) {
return 0;
if (ctx && ctx->initialized)
OnUserAlloc(thr, pc, (uptr)p, sz, true);
SignalUnsafeCall(thr, pc);
if (signal)
SignalUnsafeCall(thr, pc);
return p;
}
void user_free(ThreadState *thr, uptr pc, void *p) {
void user_free(ThreadState *thr, uptr pc, void *p, bool signal) {
if (ctx && ctx->initialized)
OnUserFree(thr, pc, (uptr)p, true);
allocator()->Deallocate(&thr->alloc_cache, p);
SignalUnsafeCall(thr, pc);
if (signal)
SignalUnsafeCall(thr, pc);
}
void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) {

View File

@ -24,9 +24,9 @@ void AllocatorPrintStats();
// For user allocations.
void *user_alloc(ThreadState *thr, uptr pc, uptr sz,
uptr align = kDefaultAlignment);
uptr align = kDefaultAlignment, bool signal = true);
// Does not accept NULL.
void user_free(ThreadState *thr, uptr pc, void *p);
void user_free(ThreadState *thr, uptr pc, void *p, bool signal = true);
void *user_realloc(ThreadState *thr, uptr pc, void *p, uptr sz);
void *user_alloc_aligned(ThreadState *thr, uptr pc, uptr sz, uptr align);
uptr user_alloc_usable_size(const void *p);

View File

@ -10,124 +10,223 @@
// Platform-specific code.
//===----------------------------------------------------------------------===//
/*
C++ linux memory layout:
0000 0000 0000 - 03c0 0000 0000: protected
03c0 0000 0000 - 1000 0000 0000: shadow
1000 0000 0000 - 3000 0000 0000: protected
3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
4000 0000 0000 - 6000 0000 0000: protected
6000 0000 0000 - 6200 0000 0000: traces
6200 0000 0000 - 7d00 0000 0000: -
7d00 0000 0000 - 7e00 0000 0000: heap
7e00 0000 0000 - 7fff ffff ffff: modules and main thread stack
Go linux and darwin memory layout:
0000 0000 0000 - 0000 1000 0000: executable
0000 1000 0000 - 00f8 0000 0000: -
00c0 0000 0000 - 00e0 0000 0000: heap
00e0 0000 0000 - 1000 0000 0000: -
1000 0000 0000 - 1380 0000 0000: shadow
1460 0000 0000 - 2000 0000 0000: -
3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
4000 0000 0000 - 6000 0000 0000: -
6000 0000 0000 - 6200 0000 0000: traces
6200 0000 0000 - 7fff ffff ffff: -
Go windows memory layout:
0000 0000 0000 - 0000 1000 0000: executable
0000 1000 0000 - 00f8 0000 0000: -
00c0 0000 0000 - 00e0 0000 0000: heap
00e0 0000 0000 - 0100 0000 0000: -
0100 0000 0000 - 0560 0000 0000: shadow
0560 0000 0000 - 0760 0000 0000: traces
0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
07d0 0000 0000 - 07ff ffff ffff: -
*/
#ifndef TSAN_PLATFORM_H
#define TSAN_PLATFORM_H
#if !defined(__LP64__) && !defined(_WIN64)
# error "Only 64-bit is supported"
#endif
#include "tsan_defs.h"
#include "tsan_trace.h"
#if defined(__LP64__) || defined(_WIN64)
namespace __tsan {
#if defined(TSAN_GO)
static const uptr kLinuxAppMemBeg = 0x000000000000ULL;
static const uptr kLinuxAppMemEnd = 0x04dfffffffffULL;
# if SANITIZER_WINDOWS
static const uptr kLinuxShadowMsk = 0x010000000000ULL;
static const uptr kMetaShadow = 0x076000000000ULL;
static const uptr kMetaSize = 0x007000000000ULL;
# else // if SANITIZER_WINDOWS
static const uptr kLinuxShadowMsk = 0x200000000000ULL;
static const uptr kMetaShadow = 0x300000000000ULL;
static const uptr kMetaSize = 0x100000000000ULL;
# endif // if SANITIZER_WINDOWS
#else // defined(TSAN_GO)
static const uptr kMetaShadow = 0x300000000000ULL;
static const uptr kMetaSize = 0x100000000000ULL;
static const uptr kLinuxAppMemBeg = 0x7cf000000000ULL;
static const uptr kLinuxAppMemEnd = 0x7fffffffffffULL;
#endif
#if !defined(TSAN_GO)
static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL;
/*
C/C++ on linux and freebsd
0000 0000 1000 - 0100 0000 0000: main binary and/or MAP_32BIT mappings
0100 0000 0000 - 0200 0000 0000: -
0200 0000 0000 - 1000 0000 0000: shadow
1000 0000 0000 - 3000 0000 0000: -
3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
4000 0000 0000 - 6000 0000 0000: -
6000 0000 0000 - 6200 0000 0000: traces
6200 0000 0000 - 7d00 0000 0000: -
7d00 0000 0000 - 7e00 0000 0000: heap
7e00 0000 0000 - 7e80 0000 0000: -
7e80 0000 0000 - 8000 0000 0000: modules and main thread stack
*/
#if SANITIZER_WINDOWS
const uptr kTraceMemBegin = 0x056000000000ULL;
#else
const uptr kTraceMemBegin = 0x600000000000ULL;
#endif
const uptr kTraceMemSize = 0x020000000000ULL;
const uptr kMetaShadowBeg = 0x300000000000ull;
const uptr kMetaShadowEnd = 0x400000000000ull;
const uptr kTraceMemBeg = 0x600000000000ull;
const uptr kTraceMemEnd = 0x620000000000ull;
const uptr kShadowBeg = 0x020000000000ull;
const uptr kShadowEnd = 0x100000000000ull;
const uptr kHeapMemBeg = 0x7d0000000000ull;
const uptr kHeapMemEnd = 0x7e0000000000ull;
const uptr kLoAppMemBeg = 0x000000001000ull;
const uptr kLoAppMemEnd = 0x010000000000ull;
const uptr kHiAppMemBeg = 0x7e8000000000ull;
const uptr kHiAppMemEnd = 0x800000000000ull;
const uptr kAppMemMsk = 0x7c0000000000ull;
const uptr kAppMemXor = 0x020000000000ull;
// This has to be a macro to allow constant initialization of constants below.
#ifndef TSAN_GO
#define MemToShadow(addr) \
((((uptr)addr) & ~(kLinuxAppMemMsk | (kShadowCell - 1))) * kShadowCnt)
#define MemToMeta(addr) \
(u32*)(((((uptr)addr) & ~(kLinuxAppMemMsk | (kMetaShadowCell - 1))) \
/ kMetaShadowCell * kMetaShadowSize) | kMetaShadow)
#else
#define MemToShadow(addr) \
(((((uptr)addr) & ~(kShadowCell - 1)) * kShadowCnt) | kLinuxShadowMsk)
#define MemToMeta(addr) \
(u32*)(((((uptr)addr) & ~(kMetaShadowCell - 1)) \
/ kMetaShadowCell * kMetaShadowSize) | kMetaShadow)
#endif
static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg);
static const uptr kLinuxShadowEnd =
MemToShadow(kLinuxAppMemEnd) | 0xff;
static inline bool IsAppMem(uptr mem) {
#if defined(TSAN_GO)
return mem <= kLinuxAppMemEnd;
#else
return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd;
#endif
ALWAYS_INLINE
bool IsAppMem(uptr mem) {
return (mem >= kHeapMemBeg && mem < kHeapMemEnd) ||
(mem >= kLoAppMemBeg && mem < kLoAppMemEnd) ||
(mem >= kHiAppMemBeg && mem < kHiAppMemEnd);
}
static inline bool IsShadowMem(uptr mem) {
return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd;
ALWAYS_INLINE
bool IsShadowMem(uptr mem) {
return mem >= kShadowBeg && mem <= kShadowEnd;
}
static inline uptr ShadowToMem(uptr shadow) {
CHECK(IsShadowMem(shadow));
#ifdef TSAN_GO
return (shadow & ~kLinuxShadowMsk) / kShadowCnt;
ALWAYS_INLINE
bool IsMetaMem(uptr mem) {
return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
}
ALWAYS_INLINE
uptr MemToShadow(uptr x) {
DCHECK(IsAppMem(x));
return (((x) & ~(kAppMemMsk | (kShadowCell - 1)))
^ kAppMemXor) * kShadowCnt;
}
ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
DCHECK(IsAppMem(x));
return (u32*)(((((x) & ~(kAppMemMsk | (kMetaShadowCell - 1)))
^ kAppMemXor) / kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
}
ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
CHECK(IsShadowMem(s));
if (s >= MemToShadow(kLoAppMemBeg) && s <= MemToShadow(kLoAppMemEnd - 1))
return (s / kShadowCnt) ^ kAppMemXor;
else
return ((s / kShadowCnt) ^ kAppMemXor) | kAppMemMsk;
}
static USED uptr UserRegions[] = {
kLoAppMemBeg, kLoAppMemEnd,
kHiAppMemBeg, kHiAppMemEnd,
kHeapMemBeg, kHeapMemEnd,
};
#elif defined(TSAN_GO) && !SANITIZER_WINDOWS
/* Go on linux, darwin and freebsd
0000 0000 1000 - 0000 1000 0000: executable
0000 1000 0000 - 00c0 0000 0000: -
00c0 0000 0000 - 00e0 0000 0000: heap
00e0 0000 0000 - 2000 0000 0000: -
2000 0000 0000 - 2380 0000 0000: shadow
2380 0000 0000 - 3000 0000 0000: -
3000 0000 0000 - 4000 0000 0000: metainfo (memory blocks and sync objects)
4000 0000 0000 - 6000 0000 0000: -
6000 0000 0000 - 6200 0000 0000: traces
6200 0000 0000 - 8000 0000 0000: -
*/
const uptr kMetaShadowBeg = 0x300000000000ull;
const uptr kMetaShadowEnd = 0x400000000000ull;
const uptr kTraceMemBeg = 0x600000000000ull;
const uptr kTraceMemEnd = 0x620000000000ull;
const uptr kShadowBeg = 0x200000000000ull;
const uptr kShadowEnd = 0x238000000000ull;
const uptr kAppMemBeg = 0x000000001000ull;
const uptr kAppMemEnd = 0x00e000000000ull;
ALWAYS_INLINE
bool IsAppMem(uptr mem) {
return mem >= kAppMemBeg && mem < kAppMemEnd;
}
ALWAYS_INLINE
bool IsShadowMem(uptr mem) {
return mem >= kShadowBeg && mem <= kShadowEnd;
}
ALWAYS_INLINE
bool IsMetaMem(uptr mem) {
return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
}
ALWAYS_INLINE
uptr MemToShadow(uptr x) {
DCHECK(IsAppMem(x));
return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
}
ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
DCHECK(IsAppMem(x));
return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
kMetaShadowCell * kMetaShadowSize) | kMetaShadowBeg);
}
ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
CHECK(IsShadowMem(s));
return (s & ~kShadowBeg) / kShadowCnt;
}
static USED uptr UserRegions[] = {
kAppMemBeg, kAppMemEnd,
};
#elif defined(TSAN_GO) && SANITIZER_WINDOWS
/* Go on windows
0000 0000 1000 - 0000 1000 0000: executable
0000 1000 0000 - 00f8 0000 0000: -
00c0 0000 0000 - 00e0 0000 0000: heap
00e0 0000 0000 - 0100 0000 0000: -
0100 0000 0000 - 0380 0000 0000: shadow
0380 0000 0000 - 0560 0000 0000: -
0560 0000 0000 - 0760 0000 0000: traces
0760 0000 0000 - 07d0 0000 0000: metainfo (memory blocks and sync objects)
07d0 0000 0000 - 8000 0000 0000: -
*/
const uptr kMetaShadowBeg = 0x076000000000ull;
const uptr kMetaShadowEnd = 0x07d000000000ull;
const uptr kTraceMemBeg = 0x056000000000ull;
const uptr kTraceMemEnd = 0x076000000000ull;
const uptr kShadowBeg = 0x010000000000ull;
const uptr kShadowEnd = 0x038000000000ull;
const uptr kAppMemBeg = 0x000000001000ull;
const uptr kAppMemEnd = 0x00e000000000ull;
ALWAYS_INLINE
bool IsAppMem(uptr mem) {
return mem >= kAppMemBeg && mem < kAppMemEnd;
}
ALWAYS_INLINE
bool IsShadowMem(uptr mem) {
return mem >= kShadowBeg && mem <= kShadowEnd;
}
ALWAYS_INLINE
bool IsMetaMem(uptr mem) {
return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd;
}
ALWAYS_INLINE
uptr MemToShadow(uptr x) {
DCHECK(IsAppMem(x));
return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg;
}
ALWAYS_INLINE
u32 *MemToMeta(uptr x) {
DCHECK(IsAppMem(x));
return (u32*)(((x & ~(kMetaShadowCell - 1)) / \
kMetaShadowCell * kMetaShadowSize) | kMetaShadowEnd);
}
ALWAYS_INLINE
uptr ShadowToMem(uptr s) {
CHECK(IsShadowMem(s));
// FIXME(dvyukov): this is most likely wrong as the mapping is not bijection.
return (x & ~kShadowBeg) / kShadowCnt;
}
static USED uptr UserRegions[] = {
kAppMemBeg, kAppMemEnd,
};
#else
return (shadow / kShadowCnt) | kLinuxAppMemMsk;
# error "Unknown platform"
#endif
}
void FlushShadowMemory();
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
uptr GetRSS();
void InitializePlatform();
void FinalizePlatform();
// The additional page is to catch shadow stack overflow as paging fault.
// Windows wants 64K alignment for mmaps.
@ -135,18 +234,23 @@ const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace)
+ (64 << 10) + (64 << 10) - 1) & ~((64 << 10) - 1);
uptr ALWAYS_INLINE GetThreadTrace(int tid) {
uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize;
DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize;
DCHECK_LT(p, kTraceMemEnd);
return p;
}
uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) {
uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize
uptr p = kTraceMemBeg + (uptr)tid * kTotalTraceSize
+ kTraceSize * sizeof(Event);
DCHECK_LT(p, kTraceMemBegin + kTraceMemSize);
DCHECK_LT(p, kTraceMemEnd);
return p;
}
void InitializePlatform();
void FlushShadowMemory();
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive);
uptr GetRSS();
void *internal_start_thread(void(*func)(void*), void *arg);
void internal_join_thread(void *th);
@ -162,8 +266,4 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
} // namespace __tsan
#else // defined(__LP64__) || defined(_WIN64)
# error "Only 64-bit is supported"
#endif
#endif // TSAN_PLATFORM_H

View File

@ -7,7 +7,7 @@
//
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
// Linux-specific code.
// Linux- and FreeBSD-specific code.
//===----------------------------------------------------------------------===//
@ -18,6 +18,7 @@
#include "sanitizer_common/sanitizer_libc.h"
#include "sanitizer_common/sanitizer_procmaps.h"
#include "sanitizer_common/sanitizer_stoptheworld.h"
#include "sanitizer_common/sanitizer_stackdepot.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "tsan_flags.h"
@ -60,6 +61,9 @@ void *__libc_stack_end = 0;
namespace __tsan {
static uptr g_data_start;
static uptr g_data_end;
const uptr kPageSize = 4096;
enum {
@ -74,22 +78,26 @@ enum {
MemCount = 8,
};
void FillProfileCallback(uptr start, uptr rss, bool file,
void FillProfileCallback(uptr p, uptr rss, bool file,
uptr *mem, uptr stats_size) {
mem[MemTotal] += rss;
start >>= 40;
if (start < 0x10)
if (p >= kShadowBeg && p < kShadowEnd)
mem[MemShadow] += rss;
else if (start >= 0x20 && start < 0x30)
mem[file ? MemFile : MemMmap] += rss;
else if (start >= 0x30 && start < 0x40)
else if (p >= kMetaShadowBeg && p < kMetaShadowEnd)
mem[MemMeta] += rss;
else if (start >= 0x7e)
mem[file ? MemFile : MemMmap] += rss;
else if (start >= 0x60 && start < 0x62)
mem[MemTrace] += rss;
else if (start >= 0x7d && start < 0x7e)
#ifndef TSAN_GO
else if (p >= kHeapMemBeg && p < kHeapMemEnd)
mem[MemHeap] += rss;
else if (p >= kLoAppMemBeg && p < kLoAppMemEnd)
mem[file ? MemFile : MemMmap] += rss;
else if (p >= kHiAppMemBeg && p < kHiAppMemEnd)
mem[file ? MemFile : MemMmap] += rss;
#else
else if (p >= kAppMemBeg && p < kAppMemEnd)
mem[file ? MemFile : MemMmap] += rss;
#endif
else if (p >= kTraceMemBeg && p < kTraceMemEnd)
mem[MemTrace] += rss;
else
mem[MemOther] += rss;
}
@ -97,12 +105,14 @@ void FillProfileCallback(uptr start, uptr rss, bool file,
void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive) {
uptr mem[MemCount] = {};
__sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7);
StackDepotStats *stacks = StackDepotGetStats();
internal_snprintf(buf, buf_size,
"RSS %zd MB: shadow:%zd meta:%zd file:%zd mmap:%zd"
" trace:%zd heap:%zd other:%zd nthr=%zd/%zd\n",
" trace:%zd heap:%zd other:%zd stacks=%zd[%zd] nthr=%zd/%zd\n",
mem[MemTotal] >> 20, mem[MemShadow] >> 20, mem[MemMeta] >> 20,
mem[MemFile] >> 20, mem[MemMmap] >> 20, mem[MemTrace] >> 20,
mem[MemHeap] >> 20, mem[MemOther] >> 20,
stacks->allocated >> 20, stacks->n_uniq_ids,
nlive, nthread);
}
@ -137,7 +147,7 @@ uptr GetRSS() {
void FlushShadowMemoryCallback(
const SuspendedThreadsList &suspended_threads_list,
void *argument) {
FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg);
FlushUnneededShadowMemory(kShadowBeg, kShadowEnd - kShadowBeg);
}
#endif
@ -218,12 +228,12 @@ static void MapRodata() {
void InitializeShadowMemory() {
// Map memory shadow.
uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg);
if (shadow != kLinuxShadowBeg) {
uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
kShadowEnd - kShadowBeg);
if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", shadow, kLinuxShadowBeg);
"to link with -pie (%p, %p).\n", shadow, kShadowBeg);
Die();
}
// This memory range is used for thread stacks and large user mmaps.
@ -235,78 +245,42 @@ void InitializeShadowMemory() {
0x10000000000ULL * kShadowMultiplier, MADV_NOHUGEPAGE);
#endif
DPrintf("memory shadow: %zx-%zx (%zuGB)\n",
kLinuxShadowBeg, kLinuxShadowEnd,
(kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
kShadowBeg, kShadowEnd,
(kShadowEnd - kShadowBeg) >> 30);
// Map meta shadow.
if (MemToMeta(kLinuxAppMemBeg) < (u32*)kMetaShadow) {
Printf("ThreadSanitizer: bad meta shadow (%p -> %p < %p)\n",
kLinuxAppMemBeg, MemToMeta(kLinuxAppMemBeg), kMetaShadow);
Die();
}
if (MemToMeta(kLinuxAppMemEnd) >= (u32*)(kMetaShadow + kMetaSize)) {
Printf("ThreadSanitizer: bad meta shadow (%p -> %p >= %p)\n",
kLinuxAppMemEnd, MemToMeta(kLinuxAppMemEnd), kMetaShadow + kMetaSize);
Die();
}
uptr meta = (uptr)MmapFixedNoReserve(kMetaShadow, kMetaSize);
if (meta != kMetaShadow) {
uptr meta_size = kMetaShadowEnd - kMetaShadowBeg;
uptr meta = (uptr)MmapFixedNoReserve(kMetaShadowBeg, meta_size);
if (meta != kMetaShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie (%p, %p).\n", meta, kMetaShadow);
"to link with -pie (%p, %p).\n", meta, kMetaShadowBeg);
Die();
}
DPrintf("meta shadow: %zx-%zx (%zuGB)\n",
kMetaShadow, kMetaShadow + kMetaSize, kMetaSize >> 30);
// Protect gaps.
const uptr kClosedLowBeg = 0x200000;
const uptr kClosedLowEnd = kLinuxShadowBeg - 1;
const uptr kClosedMidBeg = kLinuxShadowEnd + 1;
const uptr kClosedMidEnd = min(min(kLinuxAppMemBeg, kTraceMemBegin),
kMetaShadow);
ProtectRange(kClosedLowBeg, kClosedLowEnd);
ProtectRange(kClosedMidBeg, kClosedMidEnd);
VPrintf(2, "kClosedLow %zx-%zx (%zuGB)\n",
kClosedLowBeg, kClosedLowEnd, (kClosedLowEnd - kClosedLowBeg) >> 30);
VPrintf(2, "kClosedMid %zx-%zx (%zuGB)\n",
kClosedMidBeg, kClosedMidEnd, (kClosedMidEnd - kClosedMidBeg) >> 30);
VPrintf(2, "app mem: %zx-%zx (%zuGB)\n",
kLinuxAppMemBeg, kLinuxAppMemEnd,
(kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
VPrintf(2, "stack: %zx\n", (uptr)&shadow);
meta, meta + meta_size, meta_size >> 30);
MapRodata();
}
#endif
static uptr g_data_start;
static uptr g_data_end;
#ifndef TSAN_GO
static void CheckPIE() {
// Ensure that the binary is indeed compiled with -pie.
MemoryMappingLayout proc_maps(true);
uptr start, end;
if (proc_maps.Next(&start, &end,
/*offset*/0, /*filename*/0, /*filename_size*/0,
/*protection*/0)) {
if ((u64)start < kLinuxAppMemBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory ("
"something is mapped at 0x%zx < 0x%zx)\n",
start, kLinuxAppMemBeg);
Printf("FATAL: Make sure to compile with -fPIE"
" and to link with -pie.\n");
Die();
}
}
}
static void InitDataSeg() {
MemoryMappingLayout proc_maps(true);
uptr start, end, offset;
char name[128];
#if SANITIZER_FREEBSD
// On FreeBSD BSS is usually the last block allocated within the
// low range and heap is the last block allocated within the range
// 0x800000000-0x8ffffffff.
while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
/*protection*/ 0)) {
DPrintf("%p-%p %p %s\n", start, end, offset, name);
if ((start & 0xffff00000000ULL) == 0 && (end & 0xffff00000000ULL) == 0 &&
name[0] == '\0') {
g_data_start = start;
g_data_end = end;
}
}
#else
bool prev_is_data = false;
while (proc_maps.Next(&start, &end, &offset, name, ARRAY_SIZE(name),
/*protection*/ 0)) {
@ -322,12 +296,35 @@ static void InitDataSeg() {
g_data_end = end;
prev_is_data = is_data;
}
#endif
DPrintf("guessed data_start=%p data_end=%p\n", g_data_start, g_data_end);
CHECK_LT(g_data_start, g_data_end);
CHECK_GE((uptr)&g_data_start, g_data_start);
CHECK_LT((uptr)&g_data_start, g_data_end);
}
static void CheckAndProtect() {
// Ensure that the binary is indeed compiled with -pie.
MemoryMappingLayout proc_maps(true);
uptr p, end;
while (proc_maps.Next(&p, &end, 0, 0, 0, 0)) {
if (IsAppMem(p))
continue;
if (p >= kHeapMemEnd &&
p < kHeapMemEnd + PrimaryAllocator::AdditionalSize())
continue;
if (p >= 0xf000000000000000ull) // vdso
break;
Printf("FATAL: ThreadSanitizer: unexpected memory mapping %p-%p\n", p, end);
Die();
}
ProtectRange(kLoAppMemEnd, kShadowBeg);
ProtectRange(kShadowEnd, kMetaShadowBeg);
ProtectRange(kMetaShadowEnd, kTraceMemBeg);
ProtectRange(kTraceMemEnd, kHeapMemBeg);
ProtectRange(kHeapMemEnd + PrimaryAllocator::AdditionalSize(), kHiAppMemBeg);
}
#endif // #ifndef TSAN_GO
void InitializePlatform() {
@ -363,7 +360,7 @@ void InitializePlatform() {
}
#ifndef TSAN_GO
CheckPIE();
CheckAndProtect();
InitTlsSize();
InitDataSeg();
#endif
@ -426,4 +423,4 @@ int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
} // namespace __tsan
#endif // SANITIZER_LINUX
#endif // SANITIZER_LINUX || SANITIZER_FREEBSD

View File

@ -54,20 +54,20 @@ uptr GetRSS() {
#ifndef TSAN_GO
void InitializeShadowMemory() {
uptr shadow = (uptr)MmapFixedNoReserve(kLinuxShadowBeg,
kLinuxShadowEnd - kLinuxShadowBeg);
if (shadow != kLinuxShadowBeg) {
uptr shadow = (uptr)MmapFixedNoReserve(kShadowBeg,
kShadowEnd - kShadowBeg);
if (shadow != kShadowBeg) {
Printf("FATAL: ThreadSanitizer can not mmap the shadow memory\n");
Printf("FATAL: Make sure to compile with -fPIE and "
"to link with -pie.\n");
Die();
}
DPrintf("kLinuxShadow %zx-%zx (%zuGB)\n",
kLinuxShadowBeg, kLinuxShadowEnd,
(kLinuxShadowEnd - kLinuxShadowBeg) >> 30);
DPrintf("kLinuxAppMem %zx-%zx (%zuGB)\n",
kLinuxAppMemBeg, kLinuxAppMemEnd,
(kLinuxAppMemEnd - kLinuxAppMemBeg) >> 30);
DPrintf("kShadow %zx-%zx (%zuGB)\n",
kShadowBeg, kShadowEnd,
(kShadowEnd - kShadowBeg) >> 30);
DPrintf("kAppMem %zx-%zx (%zuGB)\n",
kAppMemBeg, kAppMemEnd,
(kAppMemEnd - kAppMemBeg) >> 30);
}
#endif
@ -75,10 +75,6 @@ void InitializePlatform() {
DisableCoreDumperIfNecessary();
}
void FinalizePlatform() {
fflush(0);
}
#ifndef TSAN_GO
int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m,
void *abstime), void *c, void *m, void *abstime,

View File

@ -36,10 +36,6 @@ uptr GetRSS() {
void InitializePlatform() {
}
void FinalizePlatform() {
fflush(0);
}
} // namespace __tsan
#endif // SANITIZER_WINDOWS

View File

@ -11,10 +11,30 @@
#include "tsan_report.h"
#include "tsan_platform.h"
#include "tsan_rtl.h"
#include "sanitizer_common/sanitizer_placement_new.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
namespace __tsan {
ReportStack::ReportStack() : next(nullptr), info(), suppressable(false) {}
ReportStack *ReportStack::New(uptr addr) {
void *mem = internal_alloc(MBlockReportStack, sizeof(ReportStack));
ReportStack *res = new(mem) ReportStack();
res->info.address = addr;
return res;
}
ReportLocation::ReportLocation(ReportLocationType type)
: type(type), global(), heap_chunk_start(0), heap_chunk_size(0), tid(0),
fd(0), suppressable(false), stack(nullptr) {}
ReportLocation *ReportLocation::New(ReportLocationType type) {
void *mem = internal_alloc(MBlockReportStack, sizeof(ReportLocation));
return new(mem) ReportLocation(type);
}
class Decorator: public __sanitizer::SanitizerCommonDecorator {
public:
Decorator() : SanitizerCommonDecorator() { }
@ -68,6 +88,8 @@ static const char *ReportTypeString(ReportType typ) {
return "data race on vptr (ctor/dtor vs virtual call)";
if (typ == ReportTypeUseAfterFree)
return "heap-use-after-free";
if (typ == ReportTypeVptrUseAfterFree)
return "heap-use-after-free (virtual call vs free)";
if (typ == ReportTypeThreadLeak)
return "thread leak";
if (typ == ReportTypeMutexDestroyLocked)
@ -94,14 +116,11 @@ void PrintStack(const ReportStack *ent) {
Printf(" [failed to restore the stack]\n\n");
return;
}
for (int i = 0; ent; ent = ent->next, i++) {
Printf(" #%d %s %s:%d", i, ent->func, ent->file, ent->line);
if (ent->col)
Printf(":%d", ent->col);
if (ent->module && ent->offset)
Printf(" (%s+%p)\n", ent->module, (void*)ent->offset);
else
Printf(" (%p)\n", (void*)ent->pc);
for (int i = 0; ent && ent->info.address; ent = ent->next, i++) {
InternalScopedString res(2 * GetPageSizeCached());
RenderFrame(&res, common_flags()->stack_trace_format, i, ent->info,
common_flags()->strip_path_prefix, "__interceptor_");
Printf("%s\n", res.data());
}
Printf("\n");
}
@ -143,12 +162,15 @@ static void PrintLocation(const ReportLocation *loc) {
bool print_stack = false;
Printf("%s", d.Location());
if (loc->type == ReportLocationGlobal) {
const DataInfo &global = loc->global;
Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n",
loc->name, loc->size, loc->addr, loc->module, loc->offset);
global.name, global.size, global.start,
StripModuleName(global.module), global.module_offset);
} else if (loc->type == ReportLocationHeap) {
char thrbuf[kThreadBufSize];
Printf(" Location is heap block of size %zu at %p allocated by %s:\n",
loc->size, loc->addr, thread_name(thrbuf, loc->tid));
loc->heap_chunk_size, loc->heap_chunk_start,
thread_name(thrbuf, loc->tid));
print_stack = true;
} else if (loc->type == ReportLocationStack) {
Printf(" Location is stack of %s.\n\n", thread_name(thrbuf, loc->tid));
@ -301,8 +323,10 @@ void PrintReport(const ReportDesc *rep) {
if (rep->typ == ReportTypeThreadLeak && rep->count > 1)
Printf(" And %d more similar thread leaks.\n\n", rep->count - 1);
if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep)))
ReportErrorSummary(rep_typ_str, ent->file, ent->line, ent->func);
if (ReportStack *ent = SkipTsanInternalFrames(ChooseSummaryStack(rep))) {
const AddressInfo &info = ent->info;
ReportErrorSummary(rep_typ_str, info.file, info.line, info.function);
}
Printf("==================\n");
}
@ -317,8 +341,9 @@ void PrintStack(const ReportStack *ent) {
return;
}
for (int i = 0; ent; ent = ent->next, i++) {
Printf(" %s()\n %s:%d +0x%zx\n",
ent->func, ent->file, ent->line, (void*)ent->offset);
const AddressInfo &info = ent->info;
Printf(" %s()\n %s:%d +0x%zx\n", info.function, info.file, info.line,
(void *)info.module_offset);
}
}

View File

@ -11,6 +11,7 @@
#ifndef TSAN_REPORT_H
#define TSAN_REPORT_H
#include "sanitizer_common/sanitizer_symbolizer.h"
#include "tsan_defs.h"
#include "tsan_vector.h"
@ -20,6 +21,7 @@ enum ReportType {
ReportTypeRace,
ReportTypeVptrRace,
ReportTypeUseAfterFree,
ReportTypeVptrUseAfterFree,
ReportTypeThreadLeak,
ReportTypeMutexDestroyLocked,
ReportTypeMutexDoubleLock,
@ -33,14 +35,12 @@ enum ReportType {
struct ReportStack {
ReportStack *next;
char *module;
uptr offset;
uptr pc;
char *func;
char *file;
int line;
int col;
AddressInfo info;
bool suppressable;
static ReportStack *New(uptr addr);
private:
ReportStack();
};
struct ReportMopMutex {
@ -70,17 +70,17 @@ enum ReportLocationType {
struct ReportLocation {
ReportLocationType type;
uptr addr;
uptr size;
char *module;
uptr offset;
DataInfo global;
uptr heap_chunk_start;
uptr heap_chunk_size;
int tid;
int fd;
char *name;
char *file;
int line;
bool suppressable;
ReportStack *stack;
static ReportLocation *New(ReportLocationType type);
private:
explicit ReportLocation(ReportLocationType type);
};
struct ReportThread {

View File

@ -259,8 +259,8 @@ void MapShadow(uptr addr, uptr size) {
void MapThreadTrace(uptr addr, uptr size) {
DPrintf("#0: Mapping trace at %p-%p(0x%zx)\n", addr, addr + size, size);
CHECK_GE(addr, kTraceMemBegin);
CHECK_LE(addr + size, kTraceMemBegin + kTraceMemSize);
CHECK_GE(addr, kTraceMemBeg);
CHECK_LE(addr + size, kTraceMemEnd);
CHECK_EQ(addr, addr & ~((64 << 10) - 1)); // windows wants 64K alignment
uptr addr1 = (uptr)MmapFixedNoReserve(addr, size);
if (addr1 != addr) {
@ -270,6 +270,28 @@ void MapThreadTrace(uptr addr, uptr size) {
}
}
static void CheckShadowMapping() {
for (uptr i = 0; i < ARRAY_SIZE(UserRegions); i += 2) {
const uptr beg = UserRegions[i];
const uptr end = UserRegions[i + 1];
VPrintf(3, "checking shadow region %p-%p\n", beg, end);
for (uptr p0 = beg; p0 <= end; p0 += (end - beg) / 4) {
for (int x = -1; x <= 1; x++) {
const uptr p = p0 + x;
if (p < beg || p >= end)
continue;
const uptr s = MemToShadow(p);
VPrintf(3, " checking pointer %p -> %p\n", p, s);
CHECK(IsAppMem(p));
CHECK(IsShadowMem(s));
CHECK_EQ(p & ~(kShadowCell - 1), ShadowToMem(s));
const uptr m = (uptr)MemToMeta(p);
CHECK(IsMetaMem(m));
}
}
}
}
void Initialize(ThreadState *thr) {
// Thread safe because done before all threads exist.
static bool is_initialized = false;
@ -289,6 +311,7 @@ void Initialize(ThreadState *thr) {
InitializeAllocator();
#endif
InitializeInterceptors();
CheckShadowMapping();
InitializePlatform();
InitializeMutex();
InitializeDynamicAnnotations();
@ -437,8 +460,8 @@ u32 CurrentStackId(ThreadState *thr, uptr pc) {
thr->shadow_stack_pos[0] = pc;
thr->shadow_stack_pos++;
}
u32 id = StackDepotPut(thr->shadow_stack,
thr->shadow_stack_pos - thr->shadow_stack);
u32 id = StackDepotPut(
StackTrace(thr->shadow_stack, thr->shadow_stack_pos - thr->shadow_stack));
if (pc != 0)
thr->shadow_stack_pos--;
return id;
@ -451,7 +474,7 @@ void TraceSwitch(ThreadState *thr) {
unsigned trace = (thr->fast_state.epoch() / kTracePartSize) % TraceParts();
TraceHeader *hdr = &thr_trace->headers[trace];
hdr->epoch0 = thr->fast_state.epoch();
hdr->stack0.ObtainCurrent(thr, 0);
ObtainCurrentStack(thr, 0, &hdr->stack0);
hdr->mset0 = thr->mset;
thr->nomalloc--;
}
@ -690,6 +713,8 @@ ALWAYS_INLINE
bool ContainsSameAccess(u64 *s, u64 a, u64 sync_epoch, bool is_write) {
#if defined(__SSE3__) && TSAN_SHADOW_COUNT == 4
bool res = ContainsSameAccessFast(s, a, sync_epoch, is_write);
// NOTE: this check can fail if the shadow is concurrently mutated
// by other threads.
DCHECK_EQ(res, ContainsSameAccessSlow(s, a, sync_epoch, is_write));
return res;
#else

View File

@ -51,11 +51,8 @@
namespace __tsan {
#ifndef TSAN_GO
const uptr kAllocatorSpace = 0x7d0000000000ULL;
const uptr kAllocatorSize = 0x10000000000ULL; // 1T.
struct MapUnmapCallback;
typedef SizeClassAllocator64<kAllocatorSpace, kAllocatorSize, 0,
typedef SizeClassAllocator64<kHeapMemBeg, kHeapMemEnd - kHeapMemBeg, 0,
DefaultSizeClassMap, MapUnmapCallback> PrimaryAllocator;
typedef SizeClassAllocatorLocalCache<PrimaryAllocator> AllocatorCache;
typedef LargeMmapAllocator<MapUnmapCallback> SecondaryAllocator;
@ -499,9 +496,9 @@ class ScopedReport {
explicit ScopedReport(ReportType typ);
~ScopedReport();
void AddMemoryAccess(uptr addr, Shadow s, const StackTrace *stack,
void AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
const MutexSet *mset);
void AddStack(const StackTrace *stack, bool suppressable = false);
void AddStack(StackTrace stack, bool suppressable = false);
void AddThread(const ThreadContext *tctx, bool suppressable = false);
void AddThread(int unique_tid, bool suppressable = false);
void AddUniqueTid(int unique_tid);
@ -525,7 +522,20 @@ class ScopedReport {
void operator = (const ScopedReport&);
};
void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset);
void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
MutexSet *mset);
template<typename StackTraceTy>
void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) {
uptr size = thr->shadow_stack_pos - thr->shadow_stack;
uptr start = 0;
if (size + !!toppc > kStackTraceMax) {
start = size + !!toppc - kStackTraceMax;
size = kStackTraceMax - !!toppc;
}
stack->Init(&thr->shadow_stack[start], size, toppc);
}
void StatAggregate(u64 *dst, u64 *src);
void StatOutput(u64 *stat);
@ -552,9 +562,8 @@ void ForkChildAfter(ThreadState *thr, uptr pc);
void ReportRace(ThreadState *thr);
bool OutputReport(ThreadState *thr, const ScopedReport &srep);
bool IsFiredSuppression(Context *ctx,
const ScopedReport &srep,
const StackTrace &trace);
bool IsFiredSuppression(Context *ctx, const ScopedReport &srep,
StackTrace trace);
bool IsExpectedReport(uptr addr, uptr size);
void PrintMatchedBenignRaces();
bool FrameIsInternal(const ReportStack *frame);
@ -575,7 +584,7 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent);
u32 CurrentStackId(ThreadState *thr, uptr pc);
ReportStack *SymbolizeStackId(u32 stack_id);
void PrintCurrentStack(ThreadState *thr, uptr pc);
void PrintCurrentStackSlow(); // uses libunwind
void PrintCurrentStackSlow(uptr pc); // uses libunwind
void Initialize(ThreadState *thr);
int Finalize(ThreadState *thr);

View File

@ -170,10 +170,15 @@ setjmp:
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
// obtain %rsp
#if defined(__FreeBSD__)
lea 8(%rsp), %rdi
mov %rdi, %rsi
#else
lea 16(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
#endif
// call tsan interceptor
call __tsan_setjmp
// restore env parameter
@ -197,10 +202,15 @@ _setjmp:
CFI_ADJUST_CFA_OFFSET(8)
CFI_REL_OFFSET(%rdi, 0)
// obtain %rsp
#if defined(__FreeBSD__)
lea 8(%rsp), %rdi
mov %rdi, %rsi
#else
lea 16(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
#endif
// call tsan interceptor
call __tsan_setjmp
// restore env parameter
@ -231,10 +241,15 @@ sigsetjmp:
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
// obtain %rsp
#if defined(__FreeBSD__)
lea 24(%rsp), %rdi
mov %rdi, %rsi
#else
lea 32(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
#endif
// call tsan interceptor
call __tsan_setjmp
// unalign stack frame
@ -272,10 +287,15 @@ __sigsetjmp:
sub $8, %rsp
CFI_ADJUST_CFA_OFFSET(8)
// obtain %rsp
#if defined(__FreeBSD__)
lea 24(%rsp), %rdi
mov %rdi, %rsi
#else
lea 32(%rsp), %rdi
mov %rdi, %rsi
xor %fs:0x30, %rsi // magic mangling of rsp (see libc setjmp)
rol $0x11, %rsi
#endif
// call tsan interceptor
call __tsan_setjmp
// unalign stack frame
@ -296,7 +316,7 @@ __sigsetjmp:
CFI_ENDPROC
.size __sigsetjmp, .-__sigsetjmp
#ifdef __linux__
#if defined(__FreeBSD__) || defined(__linux__)
/* We do not need executable stack. */
.section .note.GNU-stack,"",@progbits
#endif

View File

@ -57,9 +57,9 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(typ);
rep.AddMutex(mid);
StackTrace trace;
trace.ObtainCurrent(thr, pc);
rep.AddStack(&trace, true);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
rep.AddStack(trace, true);
rep.AddLocation(addr, 1);
OutputReport(thr, rep);
}
@ -122,12 +122,12 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) {
ThreadRegistryLock l(ctx->thread_registry);
ScopedReport rep(ReportTypeMutexDestroyLocked);
rep.AddMutex(mid);
StackTrace trace;
trace.ObtainCurrent(thr, pc);
rep.AddStack(&trace);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
rep.AddStack(trace);
FastState last(last_lock);
RestoreStack(last.tid(), last.epoch(), &trace, 0);
rep.AddStack(&trace, true);
rep.AddStack(trace, true);
rep.AddLocation(addr, 1);
OutputReport(thr, rep);
}
@ -470,21 +470,17 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
rep.AddUniqueTid((int)r->loop[i].thr_ctx);
rep.AddThread((int)r->loop[i].thr_ctx);
}
InternalScopedBuffer<StackTrace> stacks(2 * DDReport::kMaxLoopSize);
uptr dummy_pc = 0x42;
for (int i = 0; i < r->n; i++) {
uptr size;
for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
u32 stk = r->loop[i].stk[j];
if (stk) {
const uptr *trace = StackDepotGet(stk, &size);
stacks[i].Init(const_cast<uptr *>(trace), size);
rep.AddStack(StackDepotGet(stk), true);
} else {
// Sometimes we fail to extract the stack trace (FIXME: investigate),
// but we should still produce some stack trace in the report.
stacks[i].Init(&dummy_pc, 1);
rep.AddStack(StackTrace(&dummy_pc, 1), true);
}
rep.AddStack(&stacks[i], true);
}
}
OutputReport(thr, rep);

View File

@ -28,7 +28,7 @@ namespace __tsan {
using namespace __sanitizer; // NOLINT
static ReportStack *SymbolizeStack(const StackTrace& trace);
static ReportStack *SymbolizeStack(StackTrace trace);
void TsanCheckFailed(const char *file, int line, const char *cond,
u64 v1, u64 v2) {
@ -39,7 +39,7 @@ void TsanCheckFailed(const char *file, int line, const char *cond,
Printf("FATAL: ThreadSanitizer CHECK failed: "
"%s:%d \"%s\" (0x%zx, 0x%zx)\n",
file, line, cond, (uptr)v1, (uptr)v2);
PrintCurrentStackSlow();
PrintCurrentStackSlow(StackTrace::GetCurrentPc());
Die();
}
@ -57,27 +57,16 @@ bool WEAK OnReport(const ReportDesc *rep, bool suppressed) {
static void StackStripMain(ReportStack *stack) {
ReportStack *last_frame = 0;
ReportStack *last_frame2 = 0;
const char *prefix = "__interceptor_";
uptr prefix_len = internal_strlen(prefix);
const char *path_prefix = common_flags()->strip_path_prefix;
uptr path_prefix_len = internal_strlen(path_prefix);
char *pos;
for (ReportStack *ent = stack; ent; ent = ent->next) {
if (ent->func && 0 == internal_strncmp(ent->func, prefix, prefix_len))
ent->func += prefix_len;
if (ent->file && (pos = internal_strstr(ent->file, path_prefix)))
ent->file = pos + path_prefix_len;
if (ent->file && ent->file[0] == '.' && ent->file[1] == '/')
ent->file += 2;
last_frame2 = last_frame;
last_frame = ent;
}
if (last_frame2 == 0)
return;
const char *last = last_frame->func;
const char *last = last_frame->info.function;
#ifndef TSAN_GO
const char *last2 = last_frame2->func;
const char *last2 = last_frame2->info.function;
// Strip frame above 'main'
if (last2 && 0 == internal_strcmp(last2, "main")) {
last_frame2->next = 0;
@ -105,39 +94,36 @@ static void StackStripMain(ReportStack *stack) {
ReportStack *SymbolizeStackId(u32 stack_id) {
if (stack_id == 0)
return 0;
uptr ssz = 0;
const uptr *stack = StackDepotGet(stack_id, &ssz);
if (stack == 0)
return 0;
StackTrace trace;
trace.Init(stack, ssz);
return SymbolizeStack(trace);
StackTrace stack = StackDepotGet(stack_id);
if (stack.trace == nullptr)
return nullptr;
return SymbolizeStack(stack);
}
static ReportStack *SymbolizeStack(const StackTrace& trace) {
if (trace.IsEmpty())
static ReportStack *SymbolizeStack(StackTrace trace) {
if (trace.size == 0)
return 0;
ReportStack *stack = 0;
for (uptr si = 0; si < trace.Size(); si++) {
const uptr pc = trace.Get(si);
for (uptr si = 0; si < trace.size; si++) {
const uptr pc = trace.trace[si];
#ifndef TSAN_GO
// We obtain the return address, that is, address of the next instruction,
// so offset it by 1 byte.
const uptr pc1 = __sanitizer::StackTrace::GetPreviousInstructionPc(pc);
const uptr pc1 = StackTrace::GetPreviousInstructionPc(pc);
#else
// FIXME(dvyukov): Go sometimes uses address of a function as top pc.
uptr pc1 = pc;
if (si != trace.Size() - 1)
if (si != trace.size - 1)
pc1 -= 1;
#endif
ReportStack *ent = SymbolizeCode(pc1);
CHECK_NE(ent, 0);
ReportStack *last = ent;
while (last->next) {
last->pc = pc; // restore original pc for report
last->info.address = pc; // restore original pc for report
last = last->next;
}
last->pc = pc; // restore original pc for report
last->info.address = pc; // restore original pc for report
last->next = stack;
stack = ent;
}
@ -160,14 +146,14 @@ ScopedReport::~ScopedReport() {
DestroyAndFree(rep_);
}
void ScopedReport::AddStack(const StackTrace *stack, bool suppressable) {
void ScopedReport::AddStack(StackTrace stack, bool suppressable) {
ReportStack **rs = rep_->stacks.PushBack();
*rs = SymbolizeStack(*stack);
*rs = SymbolizeStack(stack);
(*rs)->suppressable = suppressable;
}
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
const StackTrace *stack, const MutexSet *mset) {
void ScopedReport::AddMemoryAccess(uptr addr, Shadow s, StackTrace stack,
const MutexSet *mset) {
void *mem = internal_alloc(MBlockReportMop, sizeof(ReportMop));
ReportMop *mop = new(mem) ReportMop;
rep_->mops.PushBack(mop);
@ -176,7 +162,7 @@ void ScopedReport::AddMemoryAccess(uptr addr, Shadow s,
mop->size = s.size();
mop->write = s.IsWrite();
mop->atomic = s.IsAtomic();
mop->stack = SymbolizeStack(*stack);
mop->stack = SymbolizeStack(stack);
if (mop->stack)
mop->stack->suppressable = true;
for (uptr i = 0; i < mset->Size(); i++) {
@ -315,13 +301,11 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
int creat_tid = -1;
u32 creat_stack = 0;
if (FdLocation(addr, &fd, &creat_tid, &creat_stack)) {
void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
ReportLocation *loc = new(mem) ReportLocation();
rep_->locs.PushBack(loc);
loc->type = ReportLocationFD;
ReportLocation *loc = ReportLocation::New(ReportLocationFD);
loc->fd = fd;
loc->tid = creat_tid;
loc->stack = SymbolizeStackId(creat_stack);
rep_->locs.PushBack(loc);
ThreadContext *tctx = FindThreadByUidLocked(creat_tid);
if (tctx)
AddThread(tctx);
@ -336,33 +320,25 @@ void ScopedReport::AddLocation(uptr addr, uptr size) {
}
if (b != 0) {
ThreadContext *tctx = FindThreadByTidLocked(b->tid);
void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
ReportLocation *loc = new(mem) ReportLocation();
rep_->locs.PushBack(loc);
loc->type = ReportLocationHeap;
loc->addr = (uptr)allocator()->GetBlockBegin((void*)addr);
loc->size = b->siz;
ReportLocation *loc = ReportLocation::New(ReportLocationHeap);
loc->heap_chunk_start = (uptr)allocator()->GetBlockBegin((void *)addr);
loc->heap_chunk_size = b->siz;
loc->tid = tctx ? tctx->tid : b->tid;
loc->name = 0;
loc->file = 0;
loc->line = 0;
loc->stack = 0;
loc->stack = SymbolizeStackId(b->stk);
rep_->locs.PushBack(loc);
if (tctx)
AddThread(tctx);
return;
}
bool is_stack = false;
if (ThreadContext *tctx = IsThreadStackOrTls(addr, &is_stack)) {
void *mem = internal_alloc(MBlockReportLoc, sizeof(ReportLocation));
ReportLocation *loc = new(mem) ReportLocation();
rep_->locs.PushBack(loc);
loc->type = is_stack ? ReportLocationStack : ReportLocationTLS;
ReportLocation *loc =
ReportLocation::New(is_stack ? ReportLocationStack : ReportLocationTLS);
loc->tid = tctx->tid;
rep_->locs.PushBack(loc);
AddThread(tctx);
}
ReportLocation *loc = SymbolizeData(addr);
if (loc) {
if (ReportLocation *loc = SymbolizeData(addr)) {
loc->suppressable = true;
rep_->locs.PushBack(loc);
return;
@ -384,7 +360,8 @@ const ReportDesc *ScopedReport::GetReport() const {
return rep_;
}
void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk,
MutexSet *mset) {
// This function restores stack trace and mutex set for the thread/epoch.
// It does so by getting stack trace and mutex set at the beginning of
// trace part, and then replaying the trace till the given epoch.
@ -409,13 +386,13 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n",
tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx);
InternalScopedBuffer<uptr> stack(kShadowStackSize);
for (uptr i = 0; i < hdr->stack0.Size(); i++) {
stack[i] = hdr->stack0.Get(i);
for (uptr i = 0; i < hdr->stack0.size; i++) {
stack[i] = hdr->stack0.trace[i];
DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]);
}
if (mset)
*mset = hdr->mset0;
uptr pos = hdr->stack0.Size();
uptr pos = hdr->stack0.size;
Event *events = (Event*)GetThreadTrace(tid);
for (uptr i = ebegin; i <= eend; i++) {
Event ev = events[i];
@ -450,13 +427,13 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) {
stk->Init(stack.data(), pos);
}
static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
uptr addr_min, uptr addr_max) {
static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
uptr addr_min, uptr addr_max) {
bool equal_stack = false;
RacyStacks hash;
if (flags()->suppress_equal_stacks) {
hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
for (uptr i = 0; i < ctx->racy_stacks.Size(); i++) {
if (hash == ctx->racy_stacks[i]) {
DPrintf("ThreadSanitizer: suppressing report as doubled (stack)\n");
@ -489,12 +466,12 @@ static bool HandleRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
return false;
}
static void AddRacyStacks(ThreadState *thr, const StackTrace (&traces)[2],
uptr addr_min, uptr addr_max) {
static void AddRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2],
uptr addr_min, uptr addr_max) {
if (flags()->suppress_equal_stacks) {
RacyStacks hash;
hash.hash[0] = md5_hash(traces[0].Begin(), traces[0].Size() * sizeof(uptr));
hash.hash[1] = md5_hash(traces[1].Begin(), traces[1].Size() * sizeof(uptr));
hash.hash[0] = md5_hash(traces[0].trace, traces[0].size * sizeof(uptr));
hash.hash[1] = md5_hash(traces[1].trace, traces[1].size * sizeof(uptr));
ctx->racy_stacks.PushBack(hash);
}
if (flags()->suppress_equal_addresses) {
@ -535,15 +512,14 @@ bool OutputReport(ThreadState *thr, const ScopedReport &srep) {
return true;
}
bool IsFiredSuppression(Context *ctx,
const ScopedReport &srep,
const StackTrace &trace) {
bool IsFiredSuppression(Context *ctx, const ScopedReport &srep,
StackTrace trace) {
for (uptr k = 0; k < ctx->fired_suppressions.size(); k++) {
if (ctx->fired_suppressions[k].type != srep.GetReport()->typ)
continue;
for (uptr j = 0; j < trace.Size(); j++) {
for (uptr j = 0; j < trace.size; j++) {
FiredSuppression *s = &ctx->fired_suppressions[k];
if (trace.Get(j) == s->pc) {
if (trace.trace[j] == s->pc) {
if (s->supp)
s->supp->hit_count++;
return true;
@ -570,10 +546,13 @@ static bool IsFiredSuppression(Context *ctx,
}
bool FrameIsInternal(const ReportStack *frame) {
return frame != 0 && frame->file != 0
&& (internal_strstr(frame->file, "tsan_interceptors.cc") ||
internal_strstr(frame->file, "sanitizer_common_interceptors.inc") ||
internal_strstr(frame->file, "tsan_interface_"));
if (frame == 0)
return false;
const char *file = frame->info.file;
return file != 0 &&
(internal_strstr(file, "tsan_interceptors.cc") ||
internal_strstr(file, "sanitizer_common_interceptors.inc") ||
internal_strstr(file, "tsan_interface_"));
}
static bool RaceBetweenAtomicAndFree(ThreadState *thr) {
@ -625,7 +604,9 @@ void ReportRace(ThreadState *thr) {
ThreadRegistryLock l0(ctx->thread_registry);
ReportType typ = ReportTypeRace;
if (thr->is_vptr_access)
if (thr->is_vptr_access && freed)
typ = ReportTypeVptrUseAfterFree;
else if (thr->is_vptr_access)
typ = ReportTypeVptrRace;
else if (freed)
typ = ReportTypeUseAfterFree;
@ -633,9 +614,9 @@ void ReportRace(ThreadState *thr) {
if (IsFiredSuppression(ctx, rep, addr))
return;
const uptr kMop = 2;
StackTrace traces[kMop];
VarSizeStackTrace traces[kMop];
const uptr toppc = TraceTopPC(thr);
traces[0].ObtainCurrent(thr, toppc);
ObtainCurrentStack(thr, toppc, &traces[0]);
if (IsFiredSuppression(ctx, rep, traces[0]))
return;
InternalScopedBuffer<MutexSet> mset2(1);
@ -650,7 +631,7 @@ void ReportRace(ThreadState *thr) {
for (uptr i = 0; i < kMop; i++) {
Shadow s(thr->racy_state[i]);
rep.AddMemoryAccess(addr, s, &traces[i],
rep.AddMemoryAccess(addr, s, traces[i],
i == 0 ? &thr->mset : mset2.data());
}
@ -680,26 +661,33 @@ void ReportRace(ThreadState *thr) {
}
void PrintCurrentStack(ThreadState *thr, uptr pc) {
StackTrace trace;
trace.ObtainCurrent(thr, pc);
VarSizeStackTrace trace;
ObtainCurrentStack(thr, pc, &trace);
PrintStack(SymbolizeStack(trace));
}
void PrintCurrentStackSlow() {
void PrintCurrentStackSlow(uptr pc) {
#ifndef TSAN_GO
__sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace,
sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace;
ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), 0, 0,
0, 0, false);
BufferedStackTrace *ptrace =
new(internal_alloc(MBlockStackTrace, sizeof(BufferedStackTrace)))
BufferedStackTrace();
ptrace->Unwind(kStackTraceMax, pc, 0, 0, 0, 0, false);
for (uptr i = 0; i < ptrace->size / 2; i++) {
uptr tmp = ptrace->trace[i];
ptrace->trace[i] = ptrace->trace[ptrace->size - i - 1];
ptrace->trace[ptrace->size - i - 1] = tmp;
uptr tmp = ptrace->trace_buffer[i];
ptrace->trace_buffer[i] = ptrace->trace_buffer[ptrace->size - i - 1];
ptrace->trace_buffer[ptrace->size - i - 1] = tmp;
}
StackTrace trace;
trace.Init(ptrace->trace, ptrace->size);
PrintStack(SymbolizeStack(trace));
PrintStack(SymbolizeStack(*ptrace));
#endif
}
} // namespace __tsan
using namespace __tsan;
extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
void __sanitizer_print_stack_trace() {
PrintCurrentStackSlow(StackTrace::GetCurrentPc());
}
} // extern "C"

View File

@ -8,103 +8,37 @@
// This file is a part of ThreadSanitizer (TSan), a race detector.
//
//===----------------------------------------------------------------------===//
//#include "sanitizer_common/sanitizer_placement_new.h"
#include "tsan_stack_trace.h"
#include "tsan_rtl.h"
#include "tsan_mman.h"
namespace __tsan {
StackTrace::StackTrace()
: n_()
, s_()
, c_() {
VarSizeStackTrace::VarSizeStackTrace()
: StackTrace(nullptr, 0), trace_buffer(nullptr) {}
VarSizeStackTrace::~VarSizeStackTrace() {
ResizeBuffer(0);
}
StackTrace::StackTrace(uptr *buf, uptr cnt)
: n_()
, s_(buf)
, c_(cnt) {
CHECK_NE(buf, 0);
CHECK_NE(cnt, 0);
}
StackTrace::~StackTrace() {
Reset();
}
void StackTrace::Reset() {
if (s_ && !c_) {
CHECK_NE(n_, 0);
internal_free(s_);
s_ = 0;
void VarSizeStackTrace::ResizeBuffer(uptr new_size) {
if (trace_buffer) {
internal_free(trace_buffer);
}
n_ = 0;
trace_buffer =
(new_size > 0)
? (uptr *)internal_alloc(MBlockStackTrace,
new_size * sizeof(trace_buffer[0]))
: nullptr;
trace = trace_buffer;
size = new_size;
}
void StackTrace::Init(const uptr *pcs, uptr cnt) {
Reset();
if (cnt == 0)
return;
if (c_) {
CHECK_NE(s_, 0);
CHECK_LE(cnt, c_);
} else {
s_ = (uptr*)internal_alloc(MBlockStackTrace, cnt * sizeof(s_[0]));
}
n_ = cnt;
internal_memcpy(s_, pcs, cnt * sizeof(s_[0]));
}
void StackTrace::ObtainCurrent(ThreadState *thr, uptr toppc) {
Reset();
n_ = thr->shadow_stack_pos - thr->shadow_stack;
if (n_ + !!toppc == 0)
return;
uptr start = 0;
if (c_) {
CHECK_NE(s_, 0);
if (n_ + !!toppc > c_) {
start = n_ - c_ + !!toppc;
n_ = c_ - !!toppc;
}
} else {
// Cap potentially huge stacks.
if (n_ + !!toppc > kTraceStackSize) {
start = n_ - kTraceStackSize + !!toppc;
n_ = kTraceStackSize - !!toppc;
}
s_ = (uptr*)internal_alloc(MBlockStackTrace,
(n_ + !!toppc) * sizeof(s_[0]));
}
for (uptr i = 0; i < n_; i++)
s_[i] = thr->shadow_stack[start + i];
if (toppc) {
s_[n_] = toppc;
n_++;
}
}
void StackTrace::CopyFrom(const StackTrace& other) {
Reset();
Init(other.Begin(), other.Size());
}
bool StackTrace::IsEmpty() const {
return n_ == 0;
}
uptr StackTrace::Size() const {
return n_;
}
uptr StackTrace::Get(uptr i) const {
CHECK_LT(i, n_);
return s_[i];
}
const uptr *StackTrace::Begin() const {
return s_;
void VarSizeStackTrace::Init(const uptr *pcs, uptr cnt, uptr extra_top_pc) {
ResizeBuffer(cnt + !!extra_top_pc);
internal_memcpy(trace_buffer, pcs, cnt * sizeof(trace_buffer[0]));
if (extra_top_pc)
trace_buffer[cnt] = extra_top_pc;
}
} // namespace __tsan

View File

@ -11,40 +11,25 @@
#ifndef TSAN_STACK_TRACE_H
#define TSAN_STACK_TRACE_H
//#include "sanitizer_common/sanitizer_atomic.h"
//#include "sanitizer_common/sanitizer_common.h"
//#include "sanitizer_common/sanitizer_deadlock_detector_interface.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "tsan_defs.h"
//#include "tsan_clock.h"
//#include "tsan_mutex.h"
//#include "tsan_dense_alloc.h"
namespace __tsan {
class StackTrace {
public:
StackTrace();
// Initialized the object in "static mode",
// in this mode it never calls malloc/free but uses the provided buffer.
StackTrace(uptr *buf, uptr cnt);
~StackTrace();
void Reset();
// StackTrace which calls malloc/free to allocate the buffer for
// addresses in stack traces.
struct VarSizeStackTrace : public StackTrace {
uptr *trace_buffer; // Owned.
void Init(const uptr *pcs, uptr cnt);
void ObtainCurrent(ThreadState *thr, uptr toppc);
bool IsEmpty() const;
uptr Size() const;
uptr Get(uptr i) const;
const uptr *Begin() const;
void CopyFrom(const StackTrace& other);
VarSizeStackTrace();
~VarSizeStackTrace();
void Init(const uptr *pcs, uptr cnt, uptr extra_top_pc = 0);
private:
uptr n_;
uptr *s_;
const uptr c_;
void ResizeBuffer(uptr new_size);
StackTrace(const StackTrace&);
void operator = (const StackTrace&);
VarSizeStackTrace(const VarSizeStackTrace &);
void operator=(const VarSizeStackTrace &);
};
} // namespace __tsan

View File

@ -58,6 +58,8 @@ SuppressionType conv(ReportType typ) {
return SuppressionRace;
else if (typ == ReportTypeUseAfterFree)
return SuppressionRace;
else if (typ == ReportTypeVptrUseAfterFree)
return SuppressionRace;
else if (typ == ReportTypeThreadLeak)
return SuppressionThread;
else if (typ == ReportTypeMutexDestroyLocked)
@ -89,13 +91,14 @@ uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp) {
return 0;
Suppression *s;
for (const ReportStack *frame = stack; frame; frame = frame->next) {
if (SuppressionContext::Get()->Match(frame->func, stype, &s) ||
SuppressionContext::Get()->Match(frame->file, stype, &s) ||
SuppressionContext::Get()->Match(frame->module, stype, &s)) {
const AddressInfo &info = frame->info;
if (SuppressionContext::Get()->Match(info.function, stype, &s) ||
SuppressionContext::Get()->Match(info.file, stype, &s) ||
SuppressionContext::Get()->Match(info.module, stype, &s)) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
s->hit_count++;
*sp = s;
return frame->pc;
return info.address;
}
}
return 0;
@ -109,13 +112,13 @@ uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp) {
if (stype == SuppressionNone)
return 0;
Suppression *s;
if (SuppressionContext::Get()->Match(loc->name, stype, &s) ||
SuppressionContext::Get()->Match(loc->file, stype, &s) ||
SuppressionContext::Get()->Match(loc->module, stype, &s)) {
const DataInfo &global = loc->global;
if (SuppressionContext::Get()->Match(global.name, stype, &s) ||
SuppressionContext::Get()->Match(global.module, stype, &s)) {
DPrintf("ThreadSanitizer: matched suppression '%s'\n", s->templ);
s->hit_count++;
*sp = s;
return loc->addr;
return global.start;
}
return 0;
}

View File

@ -34,38 +34,6 @@ void ExitSymbolizer() {
thr->ignore_interceptors--;
}
ReportStack *NewReportStackEntry(uptr addr) {
ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack,
sizeof(ReportStack));
internal_memset(ent, 0, sizeof(*ent));
ent->pc = addr;
return ent;
}
static ReportStack *NewReportStackEntry(const AddressInfo &info) {
ReportStack *ent = NewReportStackEntry(info.address);
ent->module = StripModuleName(info.module);
ent->offset = info.module_offset;
if (info.function)
ent->func = internal_strdup(info.function);
if (info.file)
ent->file = internal_strdup(info.file);
ent->line = info.line;
ent->col = info.column;
return ent;
}
ReportStack *next;
char *module;
uptr offset;
uptr pc;
char *func;
char *file;
int line;
int col;
// Denotes fake PC values that come from JIT/JAVA/etc.
// For such PC values __tsan_symbolize_external() will be called.
const uptr kExternalPCBit = 1ULL << 60;
@ -93,16 +61,14 @@ ReportStack *SymbolizeCode(uptr addr) {
static char func_buf[1024];
static char file_buf[1024];
int line, col;
ReportStack *ent = ReportStack::New(addr);
if (!__tsan_symbolize_external(addr, func_buf, sizeof(func_buf),
file_buf, sizeof(file_buf), &line, &col))
return NewReportStackEntry(addr);
ReportStack *ent = NewReportStackEntry(addr);
ent->module = 0;
ent->offset = 0;
ent->func = internal_strdup(func_buf);
ent->file = internal_strdup(file_buf);
ent->line = line;
ent->col = col;
return ent;
ent->info.function = internal_strdup(func_buf);
ent->info.file = internal_strdup(file_buf);
ent->info.line = line;
ent->info.column = col;
return ent;
}
static const uptr kMaxAddrFrames = 16;
@ -112,13 +78,12 @@ ReportStack *SymbolizeCode(uptr addr) {
uptr addr_frames_num = Symbolizer::GetOrInit()->SymbolizePC(
addr, addr_frames.data(), kMaxAddrFrames);
if (addr_frames_num == 0)
return NewReportStackEntry(addr);
return ReportStack::New(addr);
ReportStack *top = 0;
ReportStack *bottom = 0;
for (uptr i = 0; i < addr_frames_num; i++) {
ReportStack *cur_entry = NewReportStackEntry(addr_frames[i]);
CHECK(cur_entry);
addr_frames[i].Clear();
ReportStack *cur_entry = ReportStack::New(addr);
cur_entry->info = addr_frames[i];
if (i == 0)
top = cur_entry;
else
@ -132,16 +97,8 @@ ReportLocation *SymbolizeData(uptr addr) {
DataInfo info;
if (!Symbolizer::GetOrInit()->SymbolizeData(addr, &info))
return 0;
ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack,
sizeof(ReportLocation));
internal_memset(ent, 0, sizeof(*ent));
ent->type = ReportLocationGlobal;
ent->module = StripModuleName(info.module);
ent->offset = info.module_offset;
if (info.name)
ent->name = internal_strdup(info.name);
ent->addr = info.start;
ent->size = info.size;
ReportLocation *ent = ReportLocation::New(ReportLocationGlobal);
ent->global = info;
return ent;
}

View File

@ -40,21 +40,15 @@ enum EventType {
typedef u64 Event;
struct TraceHeader {
StackTrace stack0; // Start stack for the trace.
#ifndef TSAN_GO
BufferedStackTrace stack0; // Start stack for the trace.
#else
VarSizeStackTrace stack0;
#endif
u64 epoch0; // Start epoch for the trace.
MutexSet mset0;
#ifndef TSAN_GO
uptr stack0buf[kTraceStackSize];
#endif
TraceHeader()
#ifndef TSAN_GO
: stack0(stack0buf, kTraceStackSize)
#else
: stack0()
#endif
, epoch0() {
}
TraceHeader() : stack0(), epoch0() {}
};
struct Trace {

View File

@ -6,6 +6,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
DEFS = -D_GNU_SOURCE -D_DEBUG -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -DPIC
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic -Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti -fomit-frame-pointer -funwind-tables -fvisibility=hidden -Wno-variadic-macros
AM_CXXFLAGS += $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
AM_CXXFLAGS += -std=c++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libubsan.la

View File

@ -256,7 +256,7 @@ gcc_version := $(shell cat $(top_srcdir)/../gcc/BASE-VER)
AM_CXXFLAGS = -Wall -W -Wno-unused-parameter -Wwrite-strings -pedantic \
-Wno-long-long -fPIC -fno-builtin -fno-exceptions -fno-rtti \
-fomit-frame-pointer -funwind-tables -fvisibility=hidden \
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS)
-Wno-variadic-macros $(LIBSTDCXX_RAW_CXX_CXXFLAGS) -std=c++11
ACLOCAL_AMFLAGS = -I m4
toolexeclib_LTLIBRARIES = libubsan.la
ubsan_files = \

View File

@ -14,6 +14,7 @@
#include "ubsan_flags.h"
#include "sanitizer_common/sanitizer_report_decorator.h"
#include "sanitizer_common/sanitizer_stacktrace.h"
#include "sanitizer_common/sanitizer_stacktrace_printer.h"
#include "sanitizer_common/sanitizer_symbolizer.h"
#include <stdio.h>
@ -31,7 +32,7 @@ static void MaybePrintStackTrace(uptr pc, uptr bp) {
// under ASan).
if (StackTrace::WillUseFastUnwind(false))
return;
StackTrace stack;
BufferedStackTrace stack;
stack.Unwind(kStackTraceMax, pc, bp, 0, 0, 0, false);
stack.Print();
}
@ -44,12 +45,12 @@ static void MaybeReportErrorSummary(Location Loc) {
if (Loc.isSourceLocation()) {
SourceLocation SLoc = Loc.getSourceLocation();
if (!SLoc.isInvalid()) {
ReportErrorSummary("runtime-error", SLoc.getFilename(), SLoc.getLine(),
"");
ReportErrorSummary("undefined-behavior", SLoc.getFilename(),
SLoc.getLine(), "");
return;
}
}
ReportErrorSummary("runtime-error");
ReportErrorSummary("undefined-behavior");
}
namespace {
@ -127,14 +128,16 @@ static void renderLocation(Location Loc) {
if (SLoc.isInvalid())
LocBuffer.append("<unknown>");
else
PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
SLoc.getColumn());
RenderSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(),
SLoc.getColumn(), common_flags()->strip_path_prefix);
break;
}
case Location::LK_Module:
PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(),
Loc.getModuleLocation().getOffset());
case Location::LK_Module: {
ModuleLocation MLoc = Loc.getModuleLocation();
RenderModuleLocation(&LocBuffer, MLoc.getModuleName(), MLoc.getOffset(),
common_flags()->strip_path_prefix);
break;
}
case Location::LK_Memory:
LocBuffer.append("%p", Loc.getMemoryLocation());
break;

View File

@ -28,10 +28,10 @@ static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) {
}
namespace __ubsan {
const char *TypeCheckKinds[] = {
const char *TypeCheckKinds[] = {
"load of", "store to", "reference binding to", "member access within",
"member call on", "constructor call on", "downcast of", "downcast of"
};
"member call on", "constructor call on", "downcast of", "downcast of",
"upcast of", "cast to virtual base of"};
}
static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer,

View File

@ -113,7 +113,8 @@ __ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize];
/// \brief Determine whether \p Derived has a \p Base base class subobject at
/// offset \p Offset.
static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
static bool isDerivedFromAtOffset(sptr Object,
const abi::__class_type_info *Derived,
const abi::__class_type_info *Base,
sptr Offset) {
if (Derived->__type_name == Base->__type_name)
@ -121,7 +122,7 @@ static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
return isDerivedFromAtOffset(SI->__base_type, Base, Offset);
return isDerivedFromAtOffset(Object, SI->__base_type, Base, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
@ -136,13 +137,13 @@ static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
abi::__base_class_type_info::__virtual_mask)
// For now, just punt on virtual bases and say 'yes'.
// FIXME: OffsetHere is the offset in the vtable of the virtual base
// offset. Read the vbase offset out of the vtable and use it.
return true;
if (isDerivedFromAtOffset(VTI->base_info[base].__base_type,
Base, Offset - OffsetHere))
abi::__base_class_type_info::__virtual_mask) {
sptr VTable = *reinterpret_cast<const sptr *>(Object);
OffsetHere = *reinterpret_cast<const sptr *>(VTable + OffsetHere);
}
if (isDerivedFromAtOffset(Object + OffsetHere,
VTI->base_info[base].__base_type, Base,
Offset - OffsetHere))
return true;
}
@ -151,14 +152,15 @@ static bool isDerivedFromAtOffset(const abi::__class_type_info *Derived,
/// \brief Find the derived-most dynamic base class of \p Derived at offset
/// \p Offset.
static const abi::__class_type_info *findBaseAtOffset(
const abi::__class_type_info *Derived, sptr Offset) {
static const abi::__class_type_info *
findBaseAtOffset(sptr Object, const abi::__class_type_info *Derived,
sptr Offset) {
if (!Offset)
return Derived;
if (const abi::__si_class_type_info *SI =
dynamic_cast<const abi::__si_class_type_info*>(Derived))
return findBaseAtOffset(SI->__base_type, Offset);
return findBaseAtOffset(Object, SI->__base_type, Offset);
const abi::__vmi_class_type_info *VTI =
dynamic_cast<const abi::__vmi_class_type_info*>(Derived);
@ -170,12 +172,13 @@ static const abi::__class_type_info *findBaseAtOffset(
sptr OffsetHere = VTI->base_info[base].__offset_flags >>
abi::__base_class_type_info::__offset_shift;
if (VTI->base_info[base].__offset_flags &
abi::__base_class_type_info::__virtual_mask)
// FIXME: Can't handle virtual bases yet.
continue;
if (const abi::__class_type_info *Base =
findBaseAtOffset(VTI->base_info[base].__base_type,
Offset - OffsetHere))
abi::__base_class_type_info::__virtual_mask) {
sptr VTable = *reinterpret_cast<const sptr *>(Object);
OffsetHere = *reinterpret_cast<const sptr *>(VTable + OffsetHere);
}
if (const abi::__class_type_info *Base = findBaseAtOffset(
Object + OffsetHere, VTI->base_info[base].__base_type,
Offset - OffsetHere))
return Base;
}
@ -227,7 +230,8 @@ bool __ubsan::checkDynamicType(void *Object, void *Type, HashValue Hash) {
return false;
abi::__class_type_info *Base = (abi::__class_type_info*)Type;
if (!isDerivedFromAtOffset(Derived, Base, -Vtable->Offset))
if (!isDerivedFromAtOffset(reinterpret_cast<sptr>(Object), Derived, Base,
-Vtable->Offset))
return false;
// Success. Cache this result.
@ -241,8 +245,9 @@ __ubsan::DynamicTypeInfo __ubsan::getDynamicTypeInfo(void *Object) {
if (!Vtable)
return DynamicTypeInfo(0, 0, 0);
const abi::__class_type_info *ObjectType = findBaseAtOffset(
static_cast<const abi::__class_type_info*>(Vtable->TypeInfo),
-Vtable->Offset);
reinterpret_cast<sptr>(Object),
static_cast<const abi::__class_type_info *>(Vtable->TypeInfo),
-Vtable->Offset);
return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset,
ObjectType ? ObjectType->__type_name : "<unknown>");
}