libsanitizer merge from upstream r221802
From-SVN: r217518
This commit is contained in:
parent
47bf94b79a
commit
c5be964a42
98 changed files with 2006 additions and 1222 deletions
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 =
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 \
|
||||
|
|
|
@ -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@
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)) {
|
||||
|
|
130
libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc
Normal file
130
libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc
Normal 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
|
60
libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h
Normal file
60
libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h
Normal 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
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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).
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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) \
|
||||
|
|
|
@ -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); \
|
||||
/**/
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -36,10 +36,6 @@ uptr GetRSS() {
|
|||
void InitializePlatform() {
|
||||
}
|
||||
|
||||
void FinalizePlatform() {
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
#endif // SANITIZER_WINDOWS
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 = \
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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>");
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue