From c5be964a423f952e2ec16e2152ae504639bf8f07 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Thu, 13 Nov 2014 20:41:38 +0000 Subject: [PATCH] libsanitizer merge from upstream r221802 From-SVN: r217518 --- libsanitizer/ChangeLog | 23 ++ libsanitizer/MERGE | 2 +- libsanitizer/asan/Makefile.am | 1 + libsanitizer/asan/Makefile.in | 2 +- libsanitizer/asan/asan_allocator.h | 22 +- libsanitizer/asan/asan_allocator2.cc | 57 +-- libsanitizer/asan/asan_debugging.cc | 81 +++- libsanitizer/asan/asan_flags.h | 1 + libsanitizer/asan/asan_globals.cc | 44 ++- libsanitizer/asan/asan_interface_internal.h | 26 ++ libsanitizer/asan/asan_internal.h | 1 + libsanitizer/asan/asan_mac.cc | 2 +- libsanitizer/asan/asan_mapping.h | 15 +- libsanitizer/asan/asan_poisoning.cc | 35 +- libsanitizer/asan/asan_posix.cc | 7 +- libsanitizer/asan/asan_report.cc | 266 ++++++++----- libsanitizer/asan/asan_report.h | 57 ++- libsanitizer/asan/asan_rtl.cc | 6 +- libsanitizer/asan/asan_stack.h | 28 +- libsanitizer/asan/asan_thread.cc | 23 +- libsanitizer/asan/asan_thread.h | 26 +- .../include/sanitizer/asan_interface.h | 28 +- .../include/sanitizer/common_interface_defs.h | 2 +- libsanitizer/interception/Makefile.am | 1 + libsanitizer/interception/Makefile.in | 2 +- libsanitizer/interception/interception_win.cc | 5 +- libsanitizer/libbacktrace/Makefile.am | 1 + libsanitizer/libbacktrace/Makefile.in | 2 +- libsanitizer/lsan/Makefile.am | 1 + libsanitizer/lsan/Makefile.in | 2 +- libsanitizer/lsan/lsan.h | 22 +- libsanitizer/lsan/lsan_allocator.cc | 2 +- libsanitizer/lsan/lsan_common.cc | 25 +- libsanitizer/lsan/lsan_common_linux.cc | 7 +- libsanitizer/sanitizer_common/Makefile.am | 2 + libsanitizer/sanitizer_common/Makefile.in | 5 +- .../sanitizer_common/sanitizer_allocator.h | 15 +- .../sanitizer_common/sanitizer_common.cc | 34 +- .../sanitizer_common/sanitizer_common.h | 9 +- .../sanitizer_common_interceptors.inc | 4 + .../sanitizer_coverage_libcdep.cc | 97 ++++- .../sanitizer_coverage_mapping_libcdep.cc | 3 +- .../sanitizer_common/sanitizer_flags.cc | 12 + .../sanitizer_common/sanitizer_flags.h | 3 + .../sanitizer_common/sanitizer_mac.cc | 3 +- libsanitizer/sanitizer_common/sanitizer_mac.h | 3 +- .../sanitizer_common/sanitizer_platform.h | 10 +- .../sanitizer_platform_interceptors.h | 6 +- .../sanitizer_platform_limits_linux.cc | 3 +- .../sanitizer_platform_limits_posix.cc | 18 +- .../sanitizer_platform_limits_posix.h | 61 +-- .../sanitizer_common/sanitizer_posix.cc | 5 +- .../sanitizer_common/sanitizer_printf.cc | 2 +- .../sanitizer_common/sanitizer_stackdepot.cc | 89 ++--- .../sanitizer_common/sanitizer_stackdepot.h | 11 +- .../sanitizer_stackdepotbase.h | 4 +- .../sanitizer_common/sanitizer_stacktrace.cc | 47 ++- .../sanitizer_common/sanitizer_stacktrace.h | 56 +-- .../sanitizer_stacktrace_libcdep.cc | 56 +-- .../sanitizer_stacktrace_printer.cc | 130 ++++++ .../sanitizer_stacktrace_printer.h | 60 +++ .../sanitizer_common/sanitizer_symbolizer.h | 12 +- .../sanitizer_symbolizer_libbacktrace.cc | 2 +- .../sanitizer_symbolizer_posix_libcdep.cc | 14 +- .../sanitizer_unwind_posix_libcdep.cc | 18 +- .../sanitizer_common/sanitizer_win.cc | 8 +- libsanitizer/tsan/Makefile.am | 1 + libsanitizer/tsan/Makefile.in | 2 +- libsanitizer/tsan/tsan_defs.h | 2 - libsanitizer/tsan/tsan_fd.cc | 9 +- libsanitizer/tsan/tsan_flags.cc | 1 + libsanitizer/tsan/tsan_interceptors.cc | 370 ++++++++++++------ libsanitizer/tsan/tsan_interface_ann.cc | 4 +- libsanitizer/tsan/tsan_interface_atomic.cc | 2 +- libsanitizer/tsan/tsan_interface_java.cc | 2 +- libsanitizer/tsan/tsan_mman.cc | 16 +- libsanitizer/tsan/tsan_mman.h | 4 +- libsanitizer/tsan/tsan_platform.h | 294 +++++++++----- libsanitizer/tsan/tsan_platform_linux.cc | 149 ++++--- libsanitizer/tsan/tsan_platform_mac.cc | 22 +- libsanitizer/tsan/tsan_platform_windows.cc | 4 - libsanitizer/tsan/tsan_report.cc | 53 ++- libsanitizer/tsan/tsan_report.h | 28 +- libsanitizer/tsan/tsan_rtl.cc | 35 +- libsanitizer/tsan/tsan_rtl.h | 31 +- libsanitizer/tsan/tsan_rtl_amd64.S | 22 +- libsanitizer/tsan/tsan_rtl_mutex.cc | 22 +- libsanitizer/tsan/tsan_rtl_report.cc | 174 ++++---- libsanitizer/tsan/tsan_stack_trace.cc | 108 +---- libsanitizer/tsan/tsan_stack_trace.h | 39 +- libsanitizer/tsan/tsan_suppressions.cc | 19 +- libsanitizer/tsan/tsan_symbolize.cc | 65 +-- libsanitizer/tsan/tsan_trace.h | 18 +- libsanitizer/ubsan/Makefile.am | 1 + libsanitizer/ubsan/Makefile.in | 2 +- libsanitizer/ubsan/ubsan_diag.cc | 21 +- libsanitizer/ubsan/ubsan_handlers.cc | 6 +- libsanitizer/ubsan/ubsan_type_hash.cc | 47 ++- 98 files changed, 1992 insertions(+), 1208 deletions(-) create mode 100644 libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 0df34af898f..52d969d99e1 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,26 @@ +2014-11-13 Kostya Serebryany + + * 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 PR target/63610 diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index 48a7f26d32d..ef893cb306e 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -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. diff --git a/libsanitizer/asan/Makefile.am b/libsanitizer/asan/Makefile.am index d499c725242..b421776b99a 100644 --- a/libsanitizer/asan/Makefile.am +++ b/libsanitizer/asan/Makefile.am @@ -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 diff --git a/libsanitizer/asan/Makefile.in b/libsanitizer/asan/Makefile.in index 00a614b3d69..54ae86a5c49 100644 --- a/libsanitizer/asan/Makefile.in +++ b/libsanitizer/asan/Makefile.in @@ -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 diff --git a/libsanitizer/asan/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index 567b36867ab..d2f30af30d6 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -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); diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc index 78c1ec113a6..33d9fea70cb 100644 --- a/libsanitizer/asan/asan_allocator2.cc +++ b/libsanitizer/asan/asan_allocator2.cc @@ -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(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(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; diff --git a/libsanitizer/asan/asan_debugging.cc b/libsanitizer/asan/asan_debugging.cc index 302574d9dd8..3efad658a4c 100644 --- a/libsanitizer/asan/asan_debugging.cc +++ b/libsanitizer/asan/asan_debugging.cc @@ -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 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); diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h index 2f155eb5cbc..0b6a9857be0 100644 --- a/libsanitizer/asan/asan_flags.h +++ b/libsanitizer/asan/asan_flags.h @@ -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; diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc index 15c1886af0e..ee2ecdcc957 100644 --- a/libsanitizer/asan/asan_globals.cc +++ b/libsanitizer/asan/asan_globals.cc @@ -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 = diff --git a/libsanitizer/asan/asan_interface_internal.h b/libsanitizer/asan/asan_interface_internal.h index 1a3b33fed0b..939327eb8a4 100644 --- a/libsanitizer/asan/asan_interface_internal.h +++ b/libsanitizer/asan/asan_interface_internal.h @@ -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 diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index 9473bf6a2ca..8911575d84a 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_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; diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc index e4c71cedd31..70823bdef92 100644 --- a/libsanitizer/asan/asan_mac.cc +++ b/libsanitizer/asan/asan_mac.cc @@ -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; diff --git a/libsanitizer/asan/asan_mapping.h b/libsanitizer/asan/asan_mapping.h index a1f84e24c46..907704d7fc9 100644 --- a/libsanitizer/asan/asan_mapping.h +++ b/libsanitizer/asan/asan_mapping.h @@ -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 diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc index 65f6cf0046e..c0f3991c1f3 100644 --- a/libsanitizer/asan/asan_poisoning.cc +++ b/libsanitizer/asan/asan_poisoning.cc @@ -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(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) { diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc index 4eabb74ba80..06d24d4e0ee 100644 --- a/libsanitizer/asan/asan_posix.cc +++ b/libsanitizer/asan/asan_posix.cc @@ -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 diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 05622a12518..fcccb70ff8b 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -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 *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(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(access.frame_pc); +#endif + access.frame_pc += 16; Printf("%s", d.EndLocation()); + StackTrace alloca_stack(&access.frame_pc, 1); alloca_stack.Print(); InternalMmapVector 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(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) { diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index 4e81b9ca3a8..b31b86dd08b 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -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 *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 + ReportSanitizerGetAllocatedSizeNotOwned(uptr addr, + BufferedStackTrace *stack); void NORETURN -ReportStringFunctionSizeOverflow(uptr offset, uptr size, 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, StackTrace *stack); + 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 diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 8fccc8da967..2c599047dac 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -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()); diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index d31c0afa83b..640c4cf4f7b 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -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 diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc index 87074065633..95ca6720090 100644 --- a/libsanitizer/asan/asan_thread.cc +++ b/libsanitizer/asan/asan_thread.cc @@ -28,7 +28,7 @@ namespace __asan { void AsanThreadContext::OnCreated(void *arg) { CreateThreadContextArgs *args = static_cast(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, diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index 33242efabaa..f5ea53d051f 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -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; diff --git a/libsanitizer/include/sanitizer/asan_interface.h b/libsanitizer/include/sanitizer/asan_interface.h index d1d452991b0..023fa29c60f 100644 --- a/libsanitizer/include/sanitizer/asan_interface.h +++ b/libsanitizer/include/sanitizer/asan_interface.h @@ -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. diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index 43e0c30ee8a..3aba519327e 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -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. diff --git a/libsanitizer/interception/Makefile.am b/libsanitizer/interception/Makefile.am index e9fbe6a467d..87ee5d517d4 100644 --- a/libsanitizer/interception/Makefile.am +++ b/libsanitizer/interception/Makefile.am @@ -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 diff --git a/libsanitizer/interception/Makefile.in b/libsanitizer/interception/Makefile.in index 5ac217dcc4b..3bb0f12ba50 100644 --- a/libsanitizer/interception/Makefile.in +++ b/libsanitizer/interception/Makefile.in @@ -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 = \ diff --git a/libsanitizer/interception/interception_win.cc b/libsanitizer/interception/interception_win.cc index 1642bd116cf..f9c2e9b3ad8 100644 --- a/libsanitizer/interception/interception_win.cc +++ b/libsanitizer/interception/interception_win.cc @@ -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) { diff --git a/libsanitizer/libbacktrace/Makefile.am b/libsanitizer/libbacktrace/Makefile.am index fd9eb65f7eb..ec386f4e28b 100644 --- a/libsanitizer/libbacktrace/Makefile.am +++ b/libsanitizer/libbacktrace/Makefile.am @@ -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 diff --git a/libsanitizer/libbacktrace/Makefile.in b/libsanitizer/libbacktrace/Makefile.in index 0dfb53d2c93..a924978776e 100644 --- a/libsanitizer/libbacktrace/Makefile.in +++ b/libsanitizer/libbacktrace/Makefile.in @@ -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 \ diff --git a/libsanitizer/lsan/Makefile.am b/libsanitizer/lsan/Makefile.am index 7a508c12686..2db6ba2e762 100644 --- a/libsanitizer/lsan/Makefile.am +++ b/libsanitizer/lsan/Makefile.am @@ -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 diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in index 47caebc09f8..8ab9c685659 100644 --- a/libsanitizer/lsan/Makefile.in +++ b/libsanitizer/lsan/Makefile.in @@ -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 diff --git a/libsanitizer/lsan/lsan.h b/libsanitizer/lsan/lsan.h index 57888e3421d..ee2fc02cf08 100644 --- a/libsanitizer/lsan/lsan.h +++ b/libsanitizer/lsan/lsan.h @@ -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 \ diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc index cda2b86ef67..2d406a0f852 100644 --- a/libsanitizer/lsan/lsan_allocator.cc +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -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(m), 1, memory_order_relaxed); } diff --git a/libsanitizer/lsan/lsan_common.cc b/libsanitizer/lsan/lsan_common.cc index e340b8953e1..aa79a7e41c6 100644 --- a/libsanitizer/lsan/lsan_common.cc +++ b/libsanitizer/lsan/lsan_common.cc @@ -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; diff --git a/libsanitizer/lsan/lsan_common_linux.cc b/libsanitizer/lsan/lsan_common_linux.cc index c318fbc4c3b..b17156ce6bb 100644 --- a/libsanitizer/lsan/lsan_common_linux.cc +++ b/libsanitizer/lsan/lsan_common_linux.cc @@ -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; } diff --git a/libsanitizer/sanitizer_common/Makefile.am b/libsanitizer/sanitizer_common/Makefile.am index bc1f18c2a18..c5da0c15119 100644 --- a/libsanitizer/sanitizer_common/Makefile.am +++ b/libsanitizer/sanitizer_common/Makefile.am @@ -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 \ diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in index d82b4834efc..db02613eb81 100644 --- a/libsanitizer/sanitizer_common/Makefile.in +++ b/libsanitizer/sanitizer_common/Makefile.in @@ -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@ diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h index fb394129202..dd5539a2087 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h @@ -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(kSpaceBeg + kSpaceSize); @@ -1013,12 +1013,15 @@ class LargeMmapAllocator { if (map_size < size) return AllocatorReturnNull(); // Overflow. uptr map_beg = reinterpret_cast( 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; diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index d4da3206e13..f06efffb325 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -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) { diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index a9db1082f48..a8924913872 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -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(); diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index f55a31de77e..10f321838e8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -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 diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc index 9b02cd75017..214e8a98d0f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_coverage_libcdep.cc @@ -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(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(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 = ""; + 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 = ""; + 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(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(); diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc index a134053a719..b88814b8167 100644 --- a/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_coverage_mapping_libcdep.cc @@ -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; diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.cc b/libsanitizer/sanitizer_common/sanitizer_flags.cc index 476f793c27d..d7e7118212e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.cc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.cc @@ -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) diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.h b/libsanitizer/sanitizer_common/sanitizer_flags.h index a906c9e4535..b7a50131811 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.h +++ b/libsanitizer/sanitizer_common/sanitizer_flags.h @@ -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() { diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc index 2c2a1d74be7..17b931cb535 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cc +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc @@ -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; } } diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.h b/libsanitizer/sanitizer_common/sanitizer_mac.h index 63055297fef..47739f71c3c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.h +++ b/libsanitizer/sanitizer_common/sanitizer_mac.h @@ -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(); diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h index 14594d5ce55..7064b7166b0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform.h @@ -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 diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index 81a09927d79..fc2d7ba990c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.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 diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc index a1f04325033..8779d8adf72 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc @@ -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 #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 diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc index f5678dceccc..13d908e9f8c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -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 +#include +#endif #include #include #include @@ -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; diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h index 284be11cb5f..139fe0a1821 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h @@ -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; diff --git a/libsanitizer/sanitizer_common/sanitizer_posix.cc b/libsanitizer/sanitizer_common/sanitizer_posix.cc index 24e99f9472d..229870e00dc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_posix.cc @@ -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 diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc index fc0c357ba86..599f2c5d7c6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cc +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -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; } diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc index e1915cb808f..a3c9c0677e2 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.cc @@ -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 @@ -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 diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h index 2b1da4ee14f..aad9bfb8d35 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepot.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepot.h @@ -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 { diff --git a/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h b/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h index b9dedec2621..05b63092b80 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h +++ b/libsanitizer/sanitizer_common/sanitizer_stackdepotbase.h @@ -95,8 +95,8 @@ typename StackDepotBase::handle_type StackDepotBase::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); diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc index 0ce5ae475f6..9b99b5bb201 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc @@ -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; diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h index 0e0f1702228..31495cfc568 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -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]; - // Prints a symbolized stacktrace, followed by an empty line. - static void PrintStack(const uptr *addr, uptr size); - void Print() const { - PrintStack(trace, size); - } + StackTrace() : trace(nullptr), size(0) {} + StackTrace(const uptr *trace, uptr size) : trace(trace), size(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]; - } + // Prints a symbolized stacktrace, followed by an empty line. + 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 diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc index 5dcc0e9ed15..2d55b73f03a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -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(" \n\n"); return; } - InternalScopedBuffer buff(GetPageSizeCached() * 2); - InternalScopedBuffer addr_frames(64); + const int kMaxAddrFrames = 64; + InternalScopedBuffer 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(" ()"); - 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)) { diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc new file mode 100644 index 00000000000..300b4904c54 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.cc @@ -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("()"); + } + 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 diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h new file mode 100644 index 00000000000..54e2fb024df --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_printer.h @@ -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 , prints "in ", 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 () 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 diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h index 73a68b2ee11..2ff99372da9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.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 { diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc index 86d32e529ec..905079475b8 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc @@ -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; } diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 4f30225a4c8..ccd2d70f0f0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -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). diff --git a/libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc index c49113e1d0b..b2ca931e9d9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_unwind_posix_libcdep.cc @@ -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 diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc index 9f24510dbd0..fbccef19cb1 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_win.cc @@ -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 diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am index ab4748e0878..3896a8b9b4e 100644 --- a/libsanitizer/tsan/Makefile.am +++ b/libsanitizer/tsan/Makefile.am @@ -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 diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in index c6f1ecffa75..32e454fe291 100644 --- a/libsanitizer/tsan/Makefile.in +++ b/libsanitizer/tsan/Makefile.in @@ -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 = \ diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index a8528cbb44a..076de73c9be 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -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 { diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc index a74a668c13b..10582035f24 100644 --- a/libsanitizer/tsan/tsan_fd.cc +++ b/libsanitizer/tsan/tsan_fd.cc @@ -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 } diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc index 02bf6527e6a..6059f2771ba 100644 --- a/libsanitizer/tsan/tsan_flags.cc +++ b/libsanitizer/tsan/tsan_flags.cc @@ -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()); diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc index b49622b3ad4..7f1b6e45a8d 100644 --- a/libsanitizer/tsan/tsan_interceptors.cc +++ b/libsanitizer/tsan/tsan_interceptors.cc @@ -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 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); + return setup_at_exit_wrapper(thr, pc, (void(*)())f, 0, 0); } -TSAN_INTERCEPTOR(int, on_exit, void(*f)(int, void*), void *arg) { +TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { 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); + SCOPED_TSAN_INTERCEPTOR(__cxa_atexit, f, arg, dso); + return setup_at_exit_wrapper(thr, pc, (void(*)())f, arg, dso); } -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; +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)(at_exit_wrapper, ctx, dso); + ThreadIgnoreEnd(thr, pc); + return res; } -TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { +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(__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); - - // Dynamically load module, don't know when to call the callback for it. + 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(__cxa_atexit)(f, arg, dso); + 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) { diff --git a/libsanitizer/tsan/tsan_interface_ann.cc b/libsanitizer/tsan/tsan_interface_ann.cc index 85de2e6221f..b44e56898d6 100644 --- a/libsanitizer/tsan/tsan_interface_ann.cc +++ b/libsanitizer/tsan/tsan_interface_ann.cc @@ -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" diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc index 316614dd486..15fc21c4627 100644 --- a/libsanitizer/tsan/tsan_interface_atomic.cc +++ b/libsanitizer/tsan/tsan_interface_atomic.cc @@ -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) \ diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc index 03a9e280ddc..6e1ec6de359 100644 --- a/libsanitizer/tsan/tsan_interface_java.cc +++ b/libsanitizer/tsan/tsan_interface_java.cc @@ -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); \ /**/ diff --git a/libsanitizer/tsan/tsan_mman.cc b/libsanitizer/tsan/tsan_mman.cc index 2eae60de512..d89610a2cc3 100644 --- a/libsanitizer/tsan/tsan_mman.cc +++ b/libsanitizer/tsan/tsan_mman.cc @@ -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) { diff --git a/libsanitizer/tsan/tsan_mman.h b/libsanitizer/tsan/tsan_mman.h index ab8eb83f919..fe020af9d2d 100644 --- a/libsanitizer/tsan/tsan_mman.h +++ b/libsanitizer/tsan/tsan_mman.h @@ -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); diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index b7a6376e8ed..35837d2132a 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -10,124 +10,223 @@ // Platform-specific code. //===----------------------------------------------------------------------===// +#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" + +namespace __tsan { + +#if !defined(TSAN_GO) + /* -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 +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: protected +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 - 7fff ffff ffff: modules and main thread stack +7e00 0000 0000 - 7e80 0000 0000: - +7e80 0000 0000 - 8000 0000 0000: modules and main thread stack +*/ -Go linux and darwin memory layout: -0000 0000 0000 - 0000 1000 0000: executable -0000 1000 0000 - 00f8 0000 0000: - +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; + +ALWAYS_INLINE +bool IsAppMem(uptr mem) { + return (mem >= kHeapMemBeg && mem < kHeapMemEnd) || + (mem >= kLoAppMemBeg && mem < kLoAppMemEnd) || + (mem >= kHiAppMemBeg && mem < kHiAppMemEnd); +} + +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) & ~(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 - 1000 0000 0000: - -1000 0000 0000 - 1380 0000 0000: shadow -1460 0000 0000 - 2000 0000 0000: - +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 - 7fff ffff ffff: - +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; +} -Go windows memory layout: -0000 0000 0000 - 0000 1000 0000: executable +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 - 0560 0000 0000: shadow +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 - 07ff ffff ffff: - +07d0 0000 0000 - 8000 0000 0000: - */ -#ifndef TSAN_PLATFORM_H -#define TSAN_PLATFORM_H - -#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 +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; -static const uptr kLinuxAppMemMsk = 0x7c0000000000ULL; +ALWAYS_INLINE +bool IsAppMem(uptr mem) { + return mem >= kAppMemBeg && mem < kAppMemEnd; +} -#if SANITIZER_WINDOWS -const uptr kTraceMemBegin = 0x056000000000ULL; -#else -const uptr kTraceMemBegin = 0x600000000000ULL; -#endif -const uptr kTraceMemSize = 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 +ALWAYS_INLINE +bool IsShadowMem(uptr mem) { + return mem >= kShadowBeg && mem <= kShadowEnd; +} -static const uptr kLinuxShadowBeg = MemToShadow(kLinuxAppMemBeg); -static const uptr kLinuxShadowEnd = - MemToShadow(kLinuxAppMemEnd) | 0xff; +ALWAYS_INLINE +bool IsMetaMem(uptr mem) { + return mem >= kMetaShadowBeg && mem <= kMetaShadowEnd; +} -static inline bool IsAppMem(uptr mem) { -#if defined(TSAN_GO) - return mem <= kLinuxAppMemEnd; -#else - return mem >= kLinuxAppMemBeg && mem <= kLinuxAppMemEnd; -#endif +ALWAYS_INLINE +uptr MemToShadow(uptr x) { + DCHECK(IsAppMem(x)); + return ((x & ~(kShadowCell - 1)) * kShadowCnt) | kShadowBeg; } -static inline bool IsShadowMem(uptr mem) { - return mem >= kLinuxShadowBeg && mem <= kLinuxShadowEnd; +ALWAYS_INLINE +u32 *MemToMeta(uptr x) { + DCHECK(IsAppMem(x)); + return (u32*)(((x & ~(kMetaShadowCell - 1)) / \ + kMetaShadowCell * kMetaShadowSize) | kMetaShadowEnd); } -static inline uptr ShadowToMem(uptr shadow) { - CHECK(IsShadowMem(shadow)); -#ifdef TSAN_GO - return (shadow & ~kLinuxShadowMsk) / kShadowCnt; -#else - return (shadow / kShadowCnt) | kLinuxAppMemMsk; -#endif +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; } -void FlushShadowMemory(); -void WriteMemoryProfile(char *buf, uptr buf_size, uptr nthread, uptr nlive); -uptr GetRSS(); +static USED uptr UserRegions[] = { + kAppMemBeg, kAppMemEnd, +}; -void InitializePlatform(); -void FinalizePlatform(); +#else +# error "Unknown platform" +#endif // 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 diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc index ba81fd242e3..32591316ea2 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cc +++ b/libsanitizer/tsan/tsan_platform_linux.cc @@ -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) +#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 (start >= 0x60 && start < 0x62) + 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 if (start >= 0x7d && start < 0x7e) - mem[MemHeap] += 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 diff --git a/libsanitizer/tsan/tsan_platform_mac.cc b/libsanitizer/tsan/tsan_platform_mac.cc index 95527c79431..2091318a674 100644 --- a/libsanitizer/tsan/tsan_platform_mac.cc +++ b/libsanitizer/tsan/tsan_platform_mac.cc @@ -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, diff --git a/libsanitizer/tsan/tsan_platform_windows.cc b/libsanitizer/tsan/tsan_platform_windows.cc index d6e9e6d33f4..3f81c54e3eb 100644 --- a/libsanitizer/tsan/tsan_platform_windows.cc +++ b/libsanitizer/tsan/tsan_platform_windows.cc @@ -36,10 +36,6 @@ uptr GetRSS() { void InitializePlatform() { } -void FinalizePlatform() { - fflush(0); -} - } // namespace __tsan #endif // SANITIZER_WINDOWS diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc index b14856cd153..6feab81383f 100644 --- a/libsanitizer/tsan/tsan_report.cc +++ b/libsanitizer/tsan/tsan_report.cc @@ -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); } } diff --git a/libsanitizer/tsan/tsan_report.h b/libsanitizer/tsan/tsan_report.h index 0bde59b1675..3d99c7a7c24 100644 --- a/libsanitizer/tsan/tsan_report.h +++ b/libsanitizer/tsan/tsan_report.h @@ -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 { diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc index f5942bcaa3e..bf971b62da4 100644 --- a/libsanitizer/tsan/tsan_rtl.cc +++ b/libsanitizer/tsan/tsan_rtl.cc @@ -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 diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index c7ea94dfbde..3f7873165cd 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -51,11 +51,8 @@ namespace __tsan { #ifndef TSAN_GO -const uptr kAllocatorSpace = 0x7d0000000000ULL; -const uptr kAllocatorSize = 0x10000000000ULL; // 1T. - struct MapUnmapCallback; -typedef SizeClassAllocator64 PrimaryAllocator; typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator 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 +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); diff --git a/libsanitizer/tsan/tsan_rtl_amd64.S b/libsanitizer/tsan/tsan_rtl_amd64.S index 71a2ecda9dd..6df36a500a7 100644 --- a/libsanitizer/tsan/tsan_rtl_amd64.S +++ b/libsanitizer/tsan/tsan_rtl_amd64.S @@ -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 diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc index cc183138aba..d731b4bfb59 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cc +++ b/libsanitizer/tsan/tsan_rtl_mutex.cc @@ -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 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(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); diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc index eafd1f4dfcd..11ec8381de6 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cc +++ b/libsanitizer/tsan/tsan_rtl_report.cc @@ -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 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 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" diff --git a/libsanitizer/tsan/tsan_stack_trace.cc b/libsanitizer/tsan/tsan_stack_trace.cc index 45bd2517837..3734e0e4975 100644 --- a/libsanitizer/tsan/tsan_stack_trace.cc +++ b/libsanitizer/tsan/tsan_stack_trace.cc @@ -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_() { -} - -StackTrace::StackTrace(uptr *buf, uptr cnt) - : n_() - , s_(buf) - , c_(cnt) { - CHECK_NE(buf, 0); - CHECK_NE(cnt, 0); -} - -StackTrace::~StackTrace() { - Reset(); -} +VarSizeStackTrace::VarSizeStackTrace() + : StackTrace(nullptr, 0), trace_buffer(nullptr) {} -void StackTrace::Reset() { - if (s_ && !c_) { - CHECK_NE(n_, 0); - internal_free(s_); - s_ = 0; - } - n_ = 0; +VarSizeStackTrace::~VarSizeStackTrace() { + ResizeBuffer(0); } -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 VarSizeStackTrace::ResizeBuffer(uptr new_size) { + if (trace_buffer) { + internal_free(trace_buffer); } -} - -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_; + trace_buffer = + (new_size > 0) + ? (uptr *)internal_alloc(MBlockStackTrace, + new_size * sizeof(trace_buffer[0])) + : nullptr; + trace = trace_buffer; + size = new_size; +} + +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 diff --git a/libsanitizer/tsan/tsan_stack_trace.h b/libsanitizer/tsan/tsan_stack_trace.h index ce0cb8859d2..b097a9b2696 100644 --- a/libsanitizer/tsan/tsan_stack_trace.h +++ b/libsanitizer/tsan/tsan_stack_trace.h @@ -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(); - - 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); +// StackTrace which calls malloc/free to allocate the buffer for +// addresses in stack traces. +struct VarSizeStackTrace : public StackTrace { + uptr *trace_buffer; // Owned. + + 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 diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc index 6b42d3a67b8..76460d90d92 100644 --- a/libsanitizer/tsan/tsan_suppressions.cc +++ b/libsanitizer/tsan/tsan_suppressions.cc @@ -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; } diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc index 49ae3dffa47..795f838991b 100644 --- a/libsanitizer/tsan/tsan_symbolize.cc +++ b/libsanitizer/tsan/tsan_symbolize.cc @@ -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; } diff --git a/libsanitizer/tsan/tsan_trace.h b/libsanitizer/tsan/tsan_trace.h index af140686f5d..8eceb634a53 100644 --- a/libsanitizer/tsan/tsan_trace.h +++ b/libsanitizer/tsan/tsan_trace.h @@ -40,21 +40,15 @@ enum EventType { typedef u64 Event; struct TraceHeader { - StackTrace stack0; // Start stack for the trace. - u64 epoch0; // Start epoch for the trace. - MutexSet mset0; -#ifndef TSAN_GO - uptr stack0buf[kTraceStackSize]; -#endif - - TraceHeader() #ifndef TSAN_GO - : stack0(stack0buf, kTraceStackSize) + BufferedStackTrace stack0; // Start stack for the trace. #else - : stack0() + VarSizeStackTrace stack0; #endif - , epoch0() { - } + u64 epoch0; // Start epoch for the trace. + MutexSet mset0; + + TraceHeader() : stack0(), epoch0() {} }; struct Trace { diff --git a/libsanitizer/ubsan/Makefile.am b/libsanitizer/ubsan/Makefile.am index e1aa7080a89..ae167679cbb 100644 --- a/libsanitizer/ubsan/Makefile.am +++ b/libsanitizer/ubsan/Makefile.am @@ -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 diff --git a/libsanitizer/ubsan/Makefile.in b/libsanitizer/ubsan/Makefile.in index 02902f23ab8..543e4f1e4ce 100644 --- a/libsanitizer/ubsan/Makefile.in +++ b/libsanitizer/ubsan/Makefile.in @@ -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 = \ diff --git a/libsanitizer/ubsan/ubsan_diag.cc b/libsanitizer/ubsan/ubsan_diag.cc index 828127ab84d..028ac1a9b97 100644 --- a/libsanitizer/ubsan/ubsan_diag.cc +++ b/libsanitizer/ubsan/ubsan_diag.cc @@ -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 @@ -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(""); 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; diff --git a/libsanitizer/ubsan/ubsan_handlers.cc b/libsanitizer/ubsan/ubsan_handlers.cc index bb9322f068f..770d3c28a4a 100644 --- a/libsanitizer/ubsan/ubsan_handlers.cc +++ b/libsanitizer/ubsan/ubsan_handlers.cc @@ -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, diff --git a/libsanitizer/ubsan/ubsan_type_hash.cc b/libsanitizer/ubsan/ubsan_type_hash.cc index d01009426db..8758f6c84c5 100644 --- a/libsanitizer/ubsan/ubsan_type_hash.cc +++ b/libsanitizer/ubsan/ubsan_type_hash.cc @@ -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(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(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(Object); + OffsetHere = *reinterpret_cast(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(Derived)) - return findBaseAtOffset(SI->__base_type, Offset); + return findBaseAtOffset(Object, SI->__base_type, Offset); const abi::__vmi_class_type_info *VTI = dynamic_cast(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(Object); + OffsetHere = *reinterpret_cast(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(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(Vtable->TypeInfo), - -Vtable->Offset); + reinterpret_cast(Object), + static_cast(Vtable->TypeInfo), + -Vtable->Offset); return DynamicTypeInfo(Vtable->TypeInfo->__type_name, -Vtable->Offset, ObjectType ? ObjectType->__type_name : ""); } -- 2.30.2