From df77f0e4ec043bc4fa155efbd5c1c74ce73d2b50 Mon Sep 17 00:00:00 2001 From: Kostya Serebryany Date: Thu, 5 Dec 2013 09:18:38 +0000 Subject: libsanitizer merge from upstream r196090 From-SVN: r205695 --- libsanitizer/ChangeLog | 10 + libsanitizer/MERGE | 2 +- libsanitizer/asan/asan_allocator.h | 21 +- libsanitizer/asan/asan_allocator2.cc | 71 +- libsanitizer/asan/asan_dll_thunk.cc | 2 + libsanitizer/asan/asan_fake_stack.cc | 16 +- libsanitizer/asan/asan_fake_stack.h | 2 + libsanitizer/asan/asan_flags.h | 10 +- libsanitizer/asan/asan_globals.cc | 6 +- libsanitizer/asan/asan_interceptors.cc | 48 +- libsanitizer/asan/asan_internal.h | 2 + libsanitizer/asan/asan_linux.cc | 6 + libsanitizer/asan/asan_mac.cc | 10 +- libsanitizer/asan/asan_malloc_linux.cc | 5 +- libsanitizer/asan/asan_malloc_win.cc | 5 +- libsanitizer/asan/asan_poisoning.cc | 53 +- libsanitizer/asan/asan_poisoning.h | 3 +- libsanitizer/asan/asan_posix.cc | 15 +- libsanitizer/asan/asan_report.cc | 100 +- libsanitizer/asan/asan_report.h | 6 +- libsanitizer/asan/asan_rtl.cc | 61 +- libsanitizer/asan/asan_stack.cc | 7 +- libsanitizer/asan/asan_stack.h | 30 +- libsanitizer/asan/asan_stats.h | 4 +- libsanitizer/asan/asan_thread.cc | 53 +- libsanitizer/asan/asan_thread.h | 24 +- libsanitizer/asan/asan_win.cc | 3 + .../include/sanitizer/common_interface_defs.h | 42 +- .../include/sanitizer/linux_syscall_hooks.h | 5 +- libsanitizer/interception/interception.h | 6 + libsanitizer/interception/interception_linux.h | 9 +- libsanitizer/interception/interception_mac.h | 1 + libsanitizer/interception/interception_win.h | 3 + libsanitizer/lsan/Makefile.am | 1 + libsanitizer/lsan/Makefile.in | 4 +- libsanitizer/lsan/lsan.cc | 31 +- libsanitizer/lsan/lsan.h | 6 +- libsanitizer/lsan/lsan_allocator.cc | 9 +- libsanitizer/lsan/lsan_common.cc | 39 +- libsanitizer/lsan/lsan_common.h | 2 + libsanitizer/lsan/lsan_interceptors.cc | 54 +- libsanitizer/lsan/lsan_preinit.cc | 24 + libsanitizer/lsan/lsan_thread.cc | 4 + libsanitizer/sanitizer_common/Makefile.am | 8 +- libsanitizer/sanitizer_common/Makefile.in | 30 +- .../sanitizer_common/sanitizer_allocator.h | 72 +- .../sanitizer_allocator_internal.h | 14 +- libsanitizer/sanitizer_common/sanitizer_common.cc | 108 +- libsanitizer/sanitizer_common/sanitizer_common.h | 58 +- .../sanitizer_common_interceptors.inc | 1368 +++++++++++++++----- .../sanitizer_common_interceptors_ioctl.inc | 2 +- .../sanitizer_common/sanitizer_common_syscalls.inc | 185 ++- .../sanitizer_common/sanitizer_coverage.cc | 111 ++ libsanitizer/sanitizer_common/sanitizer_flags.cc | 30 +- libsanitizer/sanitizer_common/sanitizer_flags.h | 19 +- .../sanitizer_common/sanitizer_internal_defs.h | 32 +- libsanitizer/sanitizer_common/sanitizer_libc.cc | 20 + libsanitizer/sanitizer_common/sanitizer_libc.h | 2 + .../sanitizer_common/sanitizer_libignore.cc | 103 ++ .../sanitizer_common/sanitizer_libignore.h | 82 ++ libsanitizer/sanitizer_common/sanitizer_linux.cc | 41 +- libsanitizer/sanitizer_common/sanitizer_linux.h | 11 +- .../sanitizer_common/sanitizer_linux_libcdep.cc | 59 +- libsanitizer/sanitizer_common/sanitizer_mac.cc | 6 +- libsanitizer/sanitizer_common/sanitizer_mutex.h | 4 + .../sanitizer_common/sanitizer_placement_new.h | 10 +- libsanitizer/sanitizer_common/sanitizer_platform.h | 7 + .../sanitizer_platform_interceptors.h | 44 + .../sanitizer_platform_limits_linux.cc | 78 +- .../sanitizer_platform_limits_posix.cc | 109 +- .../sanitizer_platform_limits_posix.h | 207 ++- .../sanitizer_common/sanitizer_posix_libcdep.cc | 22 - libsanitizer/sanitizer_common/sanitizer_printf.cc | 24 +- .../sanitizer_common/sanitizer_quarantine.h | 15 +- .../sanitizer_common/sanitizer_stacktrace.cc | 204 +-- .../sanitizer_common/sanitizer_stacktrace.h | 57 +- .../sanitizer_stacktrace_libcdep.cc | 26 + .../sanitizer_stoptheworld_linux_libcdep.cc | 115 +- .../sanitizer_common/sanitizer_suppressions.cc | 9 +- .../sanitizer_common/sanitizer_suppressions.h | 5 +- .../sanitizer_common/sanitizer_symbolizer.cc | 61 + .../sanitizer_common/sanitizer_symbolizer.h | 86 +- .../sanitizer_symbolizer_libbacktrace.cc | 144 +++ .../sanitizer_symbolizer_libbacktrace.h | 38 + .../sanitizer_symbolizer_libcdep.cc | 37 + .../sanitizer_symbolizer_posix_libcdep.cc | 208 +-- .../sanitizer_common/sanitizer_symbolizer_win.cc | 7 +- .../sanitizer_syscall_linux_x86_64.inc | 16 +- .../sanitizer_common/sanitizer_thread_registry.cc | 12 + .../sanitizer_common/sanitizer_thread_registry.h | 1 + libsanitizer/sanitizer_common/sanitizer_win.cc | 30 +- libsanitizer/tsan/Makefile.am | 1 + libsanitizer/tsan/Makefile.in | 14 +- libsanitizer/tsan/tsan_defs.h | 7 +- libsanitizer/tsan/tsan_fd.cc | 34 + libsanitizer/tsan/tsan_flags.cc | 78 +- libsanitizer/tsan/tsan_flags.h | 19 +- libsanitizer/tsan/tsan_ignoreset.cc | 45 + libsanitizer/tsan/tsan_ignoreset.h | 36 + libsanitizer/tsan/tsan_interceptors.cc | 374 ++++-- libsanitizer/tsan/tsan_interface_ann.cc | 26 +- libsanitizer/tsan/tsan_interface_atomic.cc | 33 +- libsanitizer/tsan/tsan_interface_java.cc | 13 - libsanitizer/tsan/tsan_interface_java.h | 6 +- libsanitizer/tsan/tsan_mutexset.h | 2 +- libsanitizer/tsan/tsan_platform.h | 12 +- libsanitizer/tsan/tsan_platform_linux.cc | 48 +- libsanitizer/tsan/tsan_report.cc | 2 +- libsanitizer/tsan/tsan_rtl.cc | 100 +- libsanitizer/tsan/tsan_rtl.h | 40 +- libsanitizer/tsan/tsan_rtl_mutex.cc | 111 +- libsanitizer/tsan/tsan_rtl_report.cc | 58 +- libsanitizer/tsan/tsan_rtl_thread.cc | 68 +- libsanitizer/tsan/tsan_stat.cc | 59 + libsanitizer/tsan/tsan_stat.h | 59 + libsanitizer/tsan/tsan_suppressions.cc | 5 + libsanitizer/tsan/tsan_suppressions.h | 1 + libsanitizer/tsan/tsan_symbolize.cc | 51 +- libsanitizer/tsan/tsan_symbolize.h | 2 + .../tsan/tsan_symbolize_addr2line_linux.cc | 1 - libsanitizer/tsan/tsan_sync.cc | 5 + libsanitizer/tsan/tsan_trace.h | 5 + libsanitizer/ubsan/ubsan_diag.cc | 33 +- libsanitizer/ubsan/ubsan_diag.h | 6 + libsanitizer/ubsan/ubsan_handlers.cc | 31 +- libsanitizer/ubsan/ubsan_handlers.h | 11 +- libsanitizer/ubsan/ubsan_type_hash.cc | 16 +- libsanitizer/ubsan/ubsan_value.h | 4 +- 128 files changed, 4530 insertions(+), 1638 deletions(-) create mode 100644 libsanitizer/lsan/lsan_preinit.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_coverage.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_libignore.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_libignore.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc create mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h create mode 100644 libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc create mode 100644 libsanitizer/tsan/tsan_ignoreset.cc create mode 100644 libsanitizer/tsan/tsan_ignoreset.h (limited to 'libsanitizer') diff --git a/libsanitizer/ChangeLog b/libsanitizer/ChangeLog index 8404d51..661c326 100644 --- a/libsanitizer/ChangeLog +++ b/libsanitizer/ChangeLog @@ -1,3 +1,13 @@ +2013-12-05 Kostya Serebryany + + * All source files: Merge from upstream r196090. + * tsan/Makefile.am (tsan_files): Added new files. + * tsan/Makefile.in: Regenerate. + * sanitizer_common/Makefile.am (sanitizer_common_files): Added new fles. + * sanitizer_common/Makefile.in: Regenerate. + * lsan/Makefile.am (lsan_files): Added new files. + * lsan/Makefile.in: Regenerate. + 2013-11-29 Jakub Jelinek Yury Gribov diff --git a/libsanitizer/MERGE b/libsanitizer/MERGE index 0431b14..084cd2d 100644 --- a/libsanitizer/MERGE +++ b/libsanitizer/MERGE @@ -1,4 +1,4 @@ -191666 +196090 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/asan_allocator.h b/libsanitizer/asan/asan_allocator.h index 1f83dcd..763f4a5 100644 --- a/libsanitizer/asan/asan_allocator.h +++ b/libsanitizer/asan/asan_allocator.h @@ -33,10 +33,11 @@ void InitializeAllocator(); class AsanChunkView { public: explicit AsanChunkView(AsanChunk *chunk) : chunk_(chunk) {} - bool IsValid() { return chunk_ != 0; } - uptr Beg(); // first byte of user memory. - uptr End(); // last byte of user memory. - uptr UsedSize(); // size requested by the user. + bool IsValid(); // Checks if AsanChunkView points to a valid allocated + // or quarantined chunk. + uptr Beg(); // First byte of user memory. + uptr End(); // Last byte of user memory. + uptr UsedSize(); // Size requested by the user. uptr AllocTid(); uptr FreeTid(); void GetAllocStack(StackTrace *stack); @@ -88,16 +89,12 @@ class AsanChunkFifoList: public IntrusiveList { }; struct AsanThreadLocalMallocStorage { - explicit AsanThreadLocalMallocStorage(LinkerInitialized x) - { } - AsanThreadLocalMallocStorage() { - CHECK(REAL(memset)); - REAL(memset)(this, 0, sizeof(AsanThreadLocalMallocStorage)); - } - uptr quarantine_cache[16]; uptr allocator2_cache[96 * (512 * 8 + 16)]; // Opaque. void CommitBack(); + private: + // These objects are allocated via mmap() and are zero-initialized. + AsanThreadLocalMallocStorage() {} }; void *asan_memalign(uptr alignment, uptr size, StackTrace *stack, @@ -112,7 +109,7 @@ void *asan_pvalloc(uptr size, StackTrace *stack); int asan_posix_memalign(void **memptr, uptr alignment, uptr size, StackTrace *stack); -uptr asan_malloc_usable_size(void *ptr, StackTrace *stack); +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp); uptr asan_mz_size(const void *ptr); void asan_mz_force_lock(); diff --git a/libsanitizer/asan/asan_allocator2.cc b/libsanitizer/asan/asan_allocator2.cc index 34aad11..b9d66dc 100644 --- a/libsanitizer/asan/asan_allocator2.cc +++ b/libsanitizer/asan/asan_allocator2.cc @@ -92,7 +92,7 @@ AllocatorCache *GetAllocatorCache(AsanThreadLocalMallocStorage *ms) { static Allocator allocator; static const uptr kMaxAllowedMallocSize = - FIRST_32_SECOND_64(3UL << 30, 8UL << 30); + FIRST_32_SECOND_64(3UL << 30, 64UL << 30); static const uptr kMaxThreadLocalQuarantine = FIRST_32_SECOND_64(1 << 18, 1 << 20); @@ -184,14 +184,19 @@ COMPILER_CHECK(kChunkHeader2Size <= 16); struct AsanChunk: ChunkBase { uptr Beg() { return reinterpret_cast(this) + kChunkHeaderSize; } - uptr UsedSize() { + uptr UsedSize(bool locked_version = false) { if (user_requested_size != SizeClassMap::kMaxSize) return user_requested_size; - return *reinterpret_cast(allocator.GetMetaData(AllocBeg())); - } - void *AllocBeg() { - if (from_memalign) + return *reinterpret_cast( + allocator.GetMetaData(AllocBeg(locked_version))); + } + void *AllocBeg(bool locked_version = false) { + if (from_memalign) { + if (locked_version) + return allocator.GetBlockBeginFastLocked( + reinterpret_cast(this)); return allocator.GetBlockBegin(reinterpret_cast(this)); + } return reinterpret_cast(Beg() - RZLog2Size(rz_log)); } // If we don't use stack depot, we store the alloc/free stack traces @@ -211,11 +216,14 @@ struct AsanChunk: ChunkBase { uptr available = RoundUpTo(user_requested_size, SHADOW_GRANULARITY); return (available - kChunkHeader2Size) / sizeof(u32); } - bool AddrIsInside(uptr addr) { - return (addr >= Beg()) && (addr < Beg() + UsedSize()); + bool AddrIsInside(uptr addr, bool locked_version = false) { + return (addr >= Beg()) && (addr < Beg() + UsedSize(locked_version)); } }; +bool AsanChunkView::IsValid() { + return chunk_ != 0 && chunk_->chunk_state != CHUNK_AVAILABLE; +} uptr AsanChunkView::Beg() { return chunk_->Beg(); } uptr AsanChunkView::End() { return Beg() + UsedSize(); } uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); } @@ -226,25 +234,16 @@ static void GetStackTraceFromId(u32 id, StackTrace *stack) { CHECK(id); uptr size = 0; const uptr *trace = StackDepotGet(id, &size); - CHECK_LT(size, kStackTraceMax); - internal_memcpy(stack->trace, trace, sizeof(uptr) * size); - stack->size = size; + CHECK(trace); + stack->CopyFrom(trace, size); } void AsanChunkView::GetAllocStack(StackTrace *stack) { - if (flags()->use_stack_depot) - GetStackTraceFromId(chunk_->alloc_context_id, stack); - else - StackTrace::UncompressStack(stack, chunk_->AllocStackBeg(), - chunk_->AllocStackSize()); + GetStackTraceFromId(chunk_->alloc_context_id, stack); } void AsanChunkView::GetFreeStack(StackTrace *stack) { - if (flags()->use_stack_depot) - GetStackTraceFromId(chunk_->free_context_id, stack); - else - StackTrace::UncompressStack(stack, chunk_->FreeStackBeg(), - chunk_->FreeStackSize()); + GetStackTraceFromId(chunk_->free_context_id, stack); } struct QuarantineCallback; @@ -390,12 +389,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, meta[1] = chunk_beg; } - if (fl.use_stack_depot) { - m->alloc_context_id = StackDepotPut(stack->trace, stack->size); - } else { - m->alloc_context_id = 0; - StackTrace::CompressStack(stack, m->AllocStackBeg(), m->AllocStackSize()); - } + m->alloc_context_id = StackDepotPut(stack->trace, stack->size); uptr size_rounded_down_to_granularity = RoundDownTo(size, SHADOW_GRANULARITY); // Unpoison the bulk of the memory region. @@ -404,7 +398,7 @@ static void *Allocate(uptr size, uptr alignment, StackTrace *stack, // Deal with the end of the region if size is not aligned to granularity. if (size != size_rounded_down_to_granularity && fl.poison_heap) { u8 *shadow = (u8*)MemToShadow(user_beg + size_rounded_down_to_granularity); - *shadow = size & (SHADOW_GRANULARITY - 1); + *shadow = fl.poison_partial ? (size & (SHADOW_GRANULARITY - 1)) : 0; } AsanStats &thread_stats = GetCurrentThreadStats(); @@ -463,12 +457,7 @@ static void QuarantineChunk(AsanChunk *m, void *ptr, CHECK_EQ(m->free_tid, kInvalidTid); AsanThread *t = GetCurrentThread(); m->free_tid = t ? t->tid() : 0; - if (flags()->use_stack_depot) { - m->free_context_id = StackDepotPut(stack->trace, stack->size); - } else { - m->free_context_id = 0; - StackTrace::CompressStack(stack, m->FreeStackBeg(), m->FreeStackSize()); - } + m->free_context_id = StackDepotPut(stack->trace, stack->size); // Poison the region. PoisonShadow(m->Beg(), RoundUpTo(m->UsedSize(), SHADOW_GRANULARITY), @@ -673,12 +662,13 @@ int asan_posix_memalign(void **memptr, uptr alignment, uptr size, return 0; } -uptr asan_malloc_usable_size(void *ptr, StackTrace *stack) { - CHECK(stack); +uptr asan_malloc_usable_size(void *ptr, uptr pc, uptr bp) { if (ptr == 0) return 0; uptr usable_size = AllocationSize(reinterpret_cast(ptr)); - if (flags()->check_malloc_usable_size && (usable_size == 0)) - ReportMallocUsableSizeNotOwned((uptr)ptr, stack); + if (flags()->check_malloc_usable_size && (usable_size == 0)) { + GET_STACK_TRACE_FATAL(pc, bp); + ReportMallocUsableSizeNotOwned((uptr)ptr, &stack); + } return usable_size; } @@ -718,7 +708,8 @@ uptr PointsIntoChunk(void* p) { __asan::AsanChunk *m = __asan::GetAsanChunkByAddrFastLocked(addr); if (!m) return 0; uptr chunk = m->Beg(); - if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && m->AddrIsInside(addr)) + if ((m->chunk_state == __asan::CHUNK_ALLOCATED) && + m->AddrIsInside(addr, /*locked_version=*/true)) return chunk; return 0; } @@ -751,7 +742,7 @@ void LsanMetadata::set_tag(ChunkTag value) { uptr LsanMetadata::requested_size() const { __asan::AsanChunk *m = reinterpret_cast<__asan::AsanChunk *>(metadata_); - return m->UsedSize(); + return m->UsedSize(/*locked_version=*/true); } u32 LsanMetadata::stack_trace_id() const { diff --git a/libsanitizer/asan/asan_dll_thunk.cc b/libsanitizer/asan/asan_dll_thunk.cc index 26e1944..19c31f0 100644 --- a/libsanitizer/asan/asan_dll_thunk.cc +++ b/libsanitizer/asan/asan_dll_thunk.cc @@ -130,6 +130,8 @@ extern "C" { } } +WRAP_V_V(__asan_handle_no_return) + WRAP_V_W(__asan_report_store1) WRAP_V_W(__asan_report_store2) WRAP_V_W(__asan_report_store4) diff --git a/libsanitizer/asan/asan_fake_stack.cc b/libsanitizer/asan/asan_fake_stack.cc index b9cce88..cf41224 100644 --- a/libsanitizer/asan/asan_fake_stack.cc +++ b/libsanitizer/asan/asan_fake_stack.cc @@ -43,7 +43,7 @@ FakeStack *FakeStack::Create(uptr stack_size_log) { FakeStack *res = reinterpret_cast( MmapOrDie(RequiredSize(stack_size_log), "FakeStack")); res->stack_size_log_ = stack_size_log; - if (flags()->verbosity) { + if (common_flags()->verbosity) { u8 *p = reinterpret_cast(res); Report("T%d: FakeStack created: %p -- %p stack_size_log: %zd \n", GetCurrentTidOrInvalid(), p, @@ -132,6 +132,20 @@ NOINLINE void FakeStack::GC(uptr real_stack) { needs_gc_ = false; } +void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) { + for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) { + u8 *flags = GetFlags(stack_size_log(), class_id); + for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n; + i++) { + if (flags[i] == 0) continue; // not allocated. + FakeFrame *ff = reinterpret_cast( + GetFrame(stack_size_log(), class_id, i)); + uptr begin = reinterpret_cast(ff); + callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg); + } + } +} + #if SANITIZER_LINUX && !SANITIZER_ANDROID static THREADLOCAL FakeStack *fake_stack_tls; diff --git a/libsanitizer/asan/asan_fake_stack.h b/libsanitizer/asan/asan_fake_stack.h index 4287497..5196025 100644 --- a/libsanitizer/asan/asan_fake_stack.h +++ b/libsanitizer/asan/asan_fake_stack.h @@ -146,6 +146,8 @@ class FakeStack { void HandleNoReturn(); void GC(uptr real_stack); + void ForEachFakeFrame(RangeIteratorCallback callback, void *arg); + private: FakeStack() { } static const uptr kFlagsOffset = 4096; // This is were the flags begin. diff --git a/libsanitizer/asan/asan_flags.h b/libsanitizer/asan/asan_flags.h index c115997..62b5d32 100644 --- a/libsanitizer/asan/asan_flags.h +++ b/libsanitizer/asan/asan_flags.h @@ -30,8 +30,6 @@ struct Flags { // Lower value may reduce memory usage but increase the chance of // false negatives. int quarantine_size; - // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). - int verbosity; // Size (in bytes) of redzones around heap objects. // Requirement: redzone >= 32, is a power of two. int redzone; @@ -83,6 +81,9 @@ struct Flags { bool print_legend; // If set, prints ASan exit stats even after program terminates successfully. bool atexit; + // If set, coverage information will be dumped at shutdown time if the + // appropriate instrumentation was enabled. + bool coverage; // By default, disable core dumper on 64-bit - it makes little sense // to dump 16T+ core. bool disable_core; @@ -96,10 +97,11 @@ struct Flags { // Poison (or not) the heap memory on [de]allocation. Zero value is useful // for benchmarking the allocator or instrumentator. bool poison_heap; + // If true, poison partially addressable 8-byte aligned words (default=true). + // This flag affects heap and global buffers, but not stack buffers. + bool poison_partial; // Report errors on malloc/delete, new/free, new/delete[], etc. bool alloc_dealloc_mismatch; - // Use stack depot instead of storing stacks in the redzones. - bool use_stack_depot; // If true, assume that memcmp(p1, p2, n) always reads n bytes before // comparing p1 and p2. bool strict_memcmp; diff --git a/libsanitizer/asan/asan_globals.cc b/libsanitizer/asan/asan_globals.cc index 96985af..e97850a 100644 --- a/libsanitizer/asan/asan_globals.cc +++ b/libsanitizer/asan/asan_globals.cc @@ -92,15 +92,13 @@ static void RegisterGlobal(const Global *g) { CHECK(AddrIsAlignedByGranularity(g->size_with_redzone)); if (flags()->poison_heap) PoisonRedZones(*g); - ListOfGlobals *l = - (ListOfGlobals*)allocator_for_globals.Allocate(sizeof(ListOfGlobals)); + ListOfGlobals *l = new(allocator_for_globals) ListOfGlobals; l->g = g; l->next = list_of_all_globals; list_of_all_globals = l; if (g->has_dynamic_init) { if (dynamic_init_globals == 0) { - void *mem = allocator_for_globals.Allocate(sizeof(VectorOfGlobals)); - dynamic_init_globals = new(mem) + dynamic_init_globals = new(allocator_for_globals) VectorOfGlobals(kDynamicInitGlobalsInitialCapacity); } DynInitGlobal dyn_global = { *g, false }; diff --git a/libsanitizer/asan/asan_interceptors.cc b/libsanitizer/asan/asan_interceptors.cc index 72f7aae..decbfea 100644 --- a/libsanitizer/asan/asan_interceptors.cc +++ b/libsanitizer/asan/asan_interceptors.cc @@ -92,6 +92,11 @@ void SetThreadName(const char *name) { asanThreadRegistry().SetThreadName(t->tid(), name); } +int OnExit() { + // FIXME: ask frontend whether we need to return failure. + return 0; +} + } // namespace __asan // ---------------------- Wrappers ---------------- {{{1 @@ -100,6 +105,19 @@ using namespace __asan; // NOLINT DECLARE_REAL_AND_INTERCEPTOR(void *, malloc, uptr) DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) +#if !SANITIZER_MAC +#define ASAN_INTERCEPT_FUNC(name) \ + do { \ + if ((!INTERCEPT_FUNCTION(name) || !REAL(name)) && \ + common_flags()->verbosity > 0) \ + Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ + } while (0) +#else +// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. +#define ASAN_INTERCEPT_FUNC(name) +#endif // SANITIZER_MAC + +#define COMMON_INTERCEPT_FUNCTION(name) ASAN_INTERCEPT_FUNC(name) #define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ do { \ } while (false) @@ -124,16 +142,28 @@ DECLARE_REAL_AND_INTERCEPTOR(void, free, void *) do { \ } while (false) #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) SetThreadName(name) +// Should be asanThreadRegistry().SetThreadNameByUserId(thread, name) +// But asan does not remember UserId's for threads (pthread_t); +// and remembers all ever existed threads, so the linear search by UserId +// can be slow. +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + do { \ + } while (false) #define COMMON_INTERCEPTOR_BLOCK_REAL(name) REAL(name) +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) OnExit() #include "sanitizer_common/sanitizer_common_interceptors.inc" #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) ASAN_READ_RANGE(p, s) #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) ASAN_WRITE_RANGE(p, s) #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ do { \ + (void)(p); \ + (void)(s); \ } while (false) #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ do { \ + (void)(p); \ + (void)(s); \ } while (false) #include "sanitizer_common/sanitizer_common_syscalls.inc" @@ -144,8 +174,6 @@ static thread_return_t THREAD_CALLING_CONV asan_thread_start(void *arg) { } #if ASAN_INTERCEPT_PTHREAD_CREATE -extern "C" int pthread_attr_getdetachstate(void *attr, int *v); - INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { EnsureMainThreadIDIsCorrect(); @@ -155,7 +183,7 @@ INTERCEPTOR(int, pthread_create, void *thread, GET_STACK_TRACE_THREAD; int detached = 0; if (attr != 0) - pthread_attr_getdetachstate(attr, &detached); + REAL(pthread_attr_getdetachstate)(attr, &detached); u32 current_tid = GetCurrentTidOrInvalid(); AsanThread *t = AsanThread::Create(start_routine, arg); @@ -256,7 +284,7 @@ static void MlockIsUnsupported() { static bool printed = false; if (printed) return; printed = true; - if (flags()->verbosity > 0) { + if (common_flags()->verbosity > 0) { Printf("INFO: AddressSanitizer ignores " "mlock/mlockall/munlock/munlockall\n"); } @@ -645,16 +673,6 @@ INTERCEPTOR(int, __cxa_atexit, void (*func)(void *), void *arg, } #endif // ASAN_INTERCEPT___CXA_ATEXIT -#if !SANITIZER_MAC -#define ASAN_INTERCEPT_FUNC(name) do { \ - if (!INTERCEPT_FUNCTION(name) && flags()->verbosity > 0) \ - Report("AddressSanitizer: failed to intercept '" #name "'\n"); \ - } while (0) -#else -// OS X interceptors don't need to be initialized with INTERCEPT_FUNCTION. -#define ASAN_INTERCEPT_FUNC(name) -#endif // SANITIZER_MAC - #if SANITIZER_WINDOWS INTERCEPTOR_WINAPI(DWORD, CreateThread, void* security, uptr stack_size, @@ -767,7 +785,7 @@ void InitializeAsanInterceptors() { InitializeWindowsInterceptors(); #endif - if (flags()->verbosity > 0) { + if (common_flags()->verbosity > 0) { Report("AddressSanitizer: libc interceptors initialized\n"); } } diff --git a/libsanitizer/asan/asan_internal.h b/libsanitizer/asan/asan_internal.h index b5b4870..ede273a 100644 --- a/libsanitizer/asan/asan_internal.h +++ b/libsanitizer/asan/asan_internal.h @@ -96,6 +96,7 @@ void StopInitOrderChecking(); void AsanTSDInit(void (*destructor)(void *tsd)); void *AsanTSDGet(); void AsanTSDSet(void *tsd); +void PlatformTSDDtor(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); @@ -133,6 +134,7 @@ const int kAsanStackPartialRedzoneMagic = 0xf4; const int kAsanStackAfterReturnMagic = 0xf5; const int kAsanInitializationOrderMagic = 0xf6; const int kAsanUserPoisonedMemoryMagic = 0xf7; +const int kAsanContiguousContainerOOBMagic = 0xfc; const int kAsanStackUseAfterScopeMagic = 0xf8; const int kAsanGlobalRedzoneMagic = 0xf9; const int kAsanInternalHeapMagic = 0xfe; diff --git a/libsanitizer/asan/asan_linux.cc b/libsanitizer/asan/asan_linux.cc index 10c6175..0692eb1 100644 --- a/libsanitizer/asan/asan_linux.cc +++ b/libsanitizer/asan/asan_linux.cc @@ -56,6 +56,12 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp) { *pc = ucontext->uc_mcontext.arm_pc; *bp = ucontext->uc_mcontext.arm_fp; *sp = ucontext->uc_mcontext.arm_sp; +# elif defined(__hppa__) + ucontext_t *ucontext = (ucontext_t*)context; + *pc = ucontext->uc_mcontext.sc_iaoq[0]; + /* GCC uses %r3 whenever a frame pointer is needed. */ + *bp = ucontext->uc_mcontext.sc_gr[3]; + *sp = ucontext->uc_mcontext.sc_gr[30]; # elif defined(__x86_64__) ucontext_t *ucontext = (ucontext_t*)context; *pc = ucontext->uc_mcontext.gregs[REG_RIP]; diff --git a/libsanitizer/asan/asan_mac.cc b/libsanitizer/asan/asan_mac.cc index 4b28c14..8d01843 100644 --- a/libsanitizer/asan/asan_mac.cc +++ b/libsanitizer/asan/asan_mac.cc @@ -172,7 +172,7 @@ void MaybeReexec() { // Set DYLD_INSERT_LIBRARIES equal to the runtime dylib name. setenv(kDyldInsertLibraries, info.dli_fname, /*overwrite*/0); } - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Report("exec()-ing the program with\n"); Report("%s=%s\n", kDyldInsertLibraries, new_env); Report("to enable ASan wrappers.\n"); @@ -309,7 +309,7 @@ extern "C" void asan_dispatch_call_block_and_release(void *block) { GET_STACK_TRACE_THREAD; asan_block_context_t *context = (asan_block_context_t*)block; - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("asan_dispatch_call_block_and_release(): " "context: %p, pthread_self: %p\n", block, pthread_self()); @@ -344,7 +344,7 @@ asan_block_context_t *alloc_asan_context(void *ctxt, dispatch_function_t func, dispatch_function_t func) { \ GET_STACK_TRACE_THREAD; \ asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); \ - if (flags()->verbosity >= 2) { \ + if (common_flags()->verbosity >= 2) { \ Report(#dispatch_x_f "(): context: %p, pthread_self: %p\n", \ asan_ctxt, pthread_self()); \ PRINT_CURRENT_STACK(); \ @@ -362,7 +362,7 @@ INTERCEPTOR(void, dispatch_after_f, dispatch_time_t when, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("dispatch_after_f: %p\n", asan_ctxt); PRINT_CURRENT_STACK(); } @@ -375,7 +375,7 @@ INTERCEPTOR(void, dispatch_group_async_f, dispatch_group_t group, dispatch_function_t func) { GET_STACK_TRACE_THREAD; asan_block_context_t *asan_ctxt = alloc_asan_context(ctxt, func, &stack); - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("dispatch_group_async_f(): context: %p, pthread_self: %p\n", asan_ctxt, pthread_self()); PRINT_CURRENT_STACK(); diff --git a/libsanitizer/asan/asan_malloc_linux.cc b/libsanitizer/asan/asan_malloc_linux.cc index 97691fc..e3495cb 100644 --- a/libsanitizer/asan/asan_malloc_linux.cc +++ b/libsanitizer/asan/asan_malloc_linux.cc @@ -103,8 +103,9 @@ INTERCEPTOR(void*, __libc_memalign, uptr align, uptr s) ALIAS("memalign"); INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc_usable_size(ptr, &stack); + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); } // We avoid including malloc.h for portability reasons. diff --git a/libsanitizer/asan/asan_malloc_win.cc b/libsanitizer/asan/asan_malloc_win.cc index cabf8cd..1f2495f 100644 --- a/libsanitizer/asan/asan_malloc_win.cc +++ b/libsanitizer/asan/asan_malloc_win.cc @@ -96,8 +96,9 @@ void* _recalloc(void* p, size_t n, size_t elem_size) { SANITIZER_INTERFACE_ATTRIBUTE size_t _msize(void *ptr) { - GET_STACK_TRACE_MALLOC; - return asan_malloc_usable_size(ptr, &stack); + GET_CURRENT_PC_BP_SP; + (void)sp; + return asan_malloc_usable_size(ptr, pc, bp); } int _CrtDbgReport(int, const char*, int, diff --git a/libsanitizer/asan/asan_poisoning.cc b/libsanitizer/asan/asan_poisoning.cc index b967acd..86d4990 100644 --- a/libsanitizer/asan/asan_poisoning.cc +++ b/libsanitizer/asan/asan_poisoning.cc @@ -12,6 +12,7 @@ #include "asan_poisoning.h" #include "sanitizer_common/sanitizer_libc.h" +#include "sanitizer_common/sanitizer_flags.h" namespace __asan { @@ -66,7 +67,7 @@ void __asan_poison_memory_region(void const volatile *addr, uptr size) { if (!flags()->allow_user_poisoning || size == 0) return; uptr beg_addr = (uptr)addr; uptr end_addr = beg_addr + size; - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Printf("Trying to poison memory region [%p, %p)\n", (void*)beg_addr, (void*)end_addr); } @@ -108,7 +109,7 @@ void __asan_unpoison_memory_region(void const volatile *addr, uptr size) { if (!flags()->allow_user_poisoning || size == 0) return; uptr beg_addr = (uptr)addr; uptr end_addr = beg_addr + size; - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Printf("Trying to unpoison memory region [%p, %p)\n", (void*)beg_addr, (void*)end_addr); } @@ -242,13 +243,57 @@ static void PoisonAlignedStackMemory(uptr addr, uptr size, bool do_poison) { } void __asan_poison_stack_memory(uptr addr, uptr size) { - if (flags()->verbosity > 0) + if (common_flags()->verbosity > 0) Report("poisoning: %p %zx\n", (void*)addr, size); PoisonAlignedStackMemory(addr, size, true); } void __asan_unpoison_stack_memory(uptr addr, uptr size) { - if (flags()->verbosity > 0) + if (common_flags()->verbosity > 0) Report("unpoisoning: %p %zx\n", (void*)addr, size); PoisonAlignedStackMemory(addr, size, false); } + +void __sanitizer_annotate_contiguous_container(const void *beg_p, + const void *end_p, + const void *old_mid_p, + const void *new_mid_p) { + if (common_flags()->verbosity >= 2) + Printf("contiguous_container: %p %p %p %p\n", beg_p, end_p, old_mid_p, + new_mid_p); + uptr beg = reinterpret_cast(beg_p); + uptr end= reinterpret_cast(end_p); + uptr old_mid = reinterpret_cast(old_mid_p); + uptr new_mid = reinterpret_cast(new_mid_p); + uptr granularity = SHADOW_GRANULARITY; + CHECK(beg <= old_mid && beg <= new_mid && old_mid <= end && new_mid <= end && + IsAligned(beg, granularity)); + CHECK_LE(end - beg, + FIRST_32_SECOND_64(1UL << 30, 1UL << 34)); // Sanity check. + + uptr a = RoundDownTo(Min(old_mid, new_mid), granularity); + uptr c = RoundUpTo(Max(old_mid, new_mid), granularity); + uptr d1 = RoundDownTo(old_mid, granularity); + uptr d2 = RoundUpTo(old_mid, granularity); + // Currently we should be in this state: + // [a, d1) is good, [d2, c) is bad, [d1, d2) is partially good. + // Make a quick sanity check that we are indeed in this state. + if (d1 != d2) + CHECK_EQ(*(u8*)MemToShadow(d1), old_mid - d1); + if (a + granularity <= d1) + CHECK_EQ(*(u8*)MemToShadow(a), 0); + if (d2 + granularity <= c && c <= end) + CHECK_EQ(*(u8 *)MemToShadow(c - granularity), + kAsanContiguousContainerOOBMagic); + + uptr b1 = RoundDownTo(new_mid, granularity); + uptr b2 = RoundUpTo(new_mid, granularity); + // New state: + // [a, b1) is good, [b2, c) is bad, [b1, b2) is partially good. + PoisonShadow(a, b1 - a, 0); + PoisonShadow(b2, c - b2, kAsanContiguousContainerOOBMagic); + if (b1 != b2) { + CHECK_EQ(b2 - b1, granularity); + *(u8*)MemToShadow(b1) = static_cast(new_mid - b1); + } +} diff --git a/libsanitizer/asan/asan_poisoning.h b/libsanitizer/asan/asan_poisoning.h index 866c0a5..da79a0f 100644 --- a/libsanitizer/asan/asan_poisoning.h +++ b/libsanitizer/asan/asan_poisoning.h @@ -41,6 +41,7 @@ ALWAYS_INLINE void FastPoisonShadow(uptr aligned_beg, uptr aligned_size, ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( uptr aligned_addr, uptr size, uptr redzone_size, u8 value) { DCHECK(flags()->poison_heap); + bool poison_partial = flags()->poison_partial; u8 *shadow = (u8*)MEM_TO_SHADOW(aligned_addr); for (uptr i = 0; i < redzone_size; i += SHADOW_GRANULARITY, shadow++) { if (i + SHADOW_GRANULARITY <= size) { @@ -49,7 +50,7 @@ ALWAYS_INLINE void FastPoisonShadowPartialRightRedzone( *shadow = (SHADOW_GRANULARITY == 128) ? 0xff : value; // unaddressable } else { // first size-i bytes are addressable - *shadow = static_cast(size - i); + *shadow = poison_partial ? static_cast(size - i) : 0; } } } diff --git a/libsanitizer/asan/asan_posix.cc b/libsanitizer/asan/asan_posix.cc index a210a81..ac4ec9e 100644 --- a/libsanitizer/asan/asan_posix.cc +++ b/libsanitizer/asan/asan_posix.cc @@ -1,4 +1,4 @@ -//===-- asan_linux.cc -----------------------------------------------------===// +//===-- asan_posix.cc -----------------------------------------------------===// // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. @@ -42,7 +42,7 @@ static void MaybeInstallSigaction(int signum, sigact.sa_flags = SA_SIGINFO; if (flags()->use_sigaltstack) sigact.sa_flags |= SA_ONSTACK; CHECK_EQ(0, REAL(sigaction)(signum, &sigact, 0)); - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Report("Installed the sigaction for signal %d\n", signum); } } @@ -69,7 +69,7 @@ void SetAlternateSignalStack() { altstack.ss_flags = 0; altstack.ss_size = kAltStackSize; CHECK_EQ(0, sigaltstack(&altstack, 0)); - if (flags()->verbosity > 0) { + if (common_flags()->verbosity > 0) { Report("Alternative stack for T%d set: [%p,%p)\n", GetCurrentTidOrInvalid(), altstack.ss_sp, (char*)altstack.ss_sp + altstack.ss_size); @@ -114,6 +114,15 @@ void AsanTSDSet(void *tsd) { pthread_setspecific(tsd_key, tsd); } +void PlatformTSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (context->destructor_iterations > 1) { + context->destructor_iterations--; + CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); + return; + } + AsanThread::TSDDtor(tsd); +} } // namespace __asan #endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/libsanitizer/asan/asan_report.cc b/libsanitizer/asan/asan_report.cc index 8f11ff4..70c4b48 100644 --- a/libsanitizer/asan/asan_report.cc +++ b/libsanitizer/asan/asan_report.cc @@ -18,6 +18,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_flags.h" #include "sanitizer_common/sanitizer_report_decorator.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" namespace __asan { @@ -71,6 +72,7 @@ class Decorator: private __sanitizer::AnsiColorDecorator { case kAsanInitializationOrderMagic: return Cyan(); case kAsanUserPoisonedMemoryMagic: + case kAsanContiguousContainerOOBMagic: return Blue(); case kAsanStackUseAfterScopeMagic: return Magenta(); @@ -117,19 +119,21 @@ static void PrintLegend() { for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte("", i, " "); Printf("\n"); - PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); - PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic); - PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic); - PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic); - PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic); - PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic); - PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic); - PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic); - PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic); - PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic); - PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic); - PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic); - PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic); + PrintShadowByte(" Heap left redzone: ", kAsanHeapLeftRedzoneMagic); + PrintShadowByte(" Heap right redzone: ", kAsanHeapRightRedzoneMagic); + PrintShadowByte(" Freed heap region: ", kAsanHeapFreeMagic); + PrintShadowByte(" Stack left redzone: ", kAsanStackLeftRedzoneMagic); + PrintShadowByte(" Stack mid redzone: ", kAsanStackMidRedzoneMagic); + PrintShadowByte(" Stack right redzone: ", kAsanStackRightRedzoneMagic); + PrintShadowByte(" Stack partial redzone: ", kAsanStackPartialRedzoneMagic); + PrintShadowByte(" Stack after return: ", kAsanStackAfterReturnMagic); + PrintShadowByte(" Stack use after scope: ", kAsanStackUseAfterScopeMagic); + PrintShadowByte(" Global redzone: ", kAsanGlobalRedzoneMagic); + PrintShadowByte(" Global init order: ", kAsanInitializationOrderMagic); + PrintShadowByte(" Poisoned by user: ", kAsanUserPoisonedMemoryMagic); + PrintShadowByte(" Contiguous container OOB:", + kAsanContiguousContainerOOBMagic); + PrintShadowByte(" ASan internal: ", kAsanInternalHeapMagic); } static void PrintShadowMemoryForAddress(uptr addr) { @@ -178,8 +182,8 @@ static bool IsASCII(unsigned char c) { static const char *MaybeDemangleGlobalName(const char *name) { // We can spoil names of globals with C linkage, so use an heuristic // approach to check if the name should be demangled. - return (name[0] == '_' && name[1] == 'Z' && &getSymbolizer) - ? getSymbolizer()->Demangle(name) + return (name[0] == '_' && name[1] == 'Z') + ? Symbolizer::Get()->Demangle(name) : name; } @@ -412,7 +416,11 @@ static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr, void DescribeHeapAddress(uptr addr, uptr access_size) { AsanChunkView chunk = FindHeapChunkByAddress(addr); - if (!chunk.IsValid()) return; + if (!chunk.IsValid()) { + Printf("AddressSanitizer can not describe address in more detail " + "(wild memory access suspected).\n"); + return; + } DescribeAccessToHeapChunk(chunk, addr, access_size); CHECK(chunk.AllocTid() != kInvalidTid); asanThreadRegistry().CheckLocked(); @@ -479,7 +487,9 @@ void DescribeThread(AsanThreadContext *context) { context->parent_tid, ThreadNameWithParenthesis(context->parent_tid, tname, sizeof(tname))); - PrintStack(&context->stack); + uptr stack_size; + const uptr *stack_trace = StackDepotGet(context->stack_id, &stack_size); + PrintStack(stack_trace, stack_size); // Recursively described parent thread if needed. if (flags()->print_full_thread_history) { AsanThreadContext *parent_context = @@ -540,22 +550,6 @@ class ScopedInErrorReport { } }; -static void ReportSummary(const char *error_type, StackTrace *stack) { - if (!stack->size) return; - if (&getSymbolizer && getSymbolizer()->IsAvailable()) { - AddressInfo ai; - // Currently, we include the first stack frame into the report summary. - // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - getSymbolizer()->SymbolizeCode(pc, &ai, 1); - ReportErrorSummary(error_type, - StripPathPrefix(ai.file, - common_flags()->strip_path_prefix), - ai.line, ai.function); - } - // FIXME: do we need to print anything at all if there is no symbolizer? -} - void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { ScopedInErrorReport in_report; Decorator d; @@ -565,13 +559,13 @@ void ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr) { (void*)addr, (void*)pc, (void*)sp, (void*)bp, GetCurrentTidOrInvalid()); Printf("%s", d.EndWarning()); - Printf("AddressSanitizer can not provide additional info.\n"); GET_STACK_TRACE_FATAL(pc, bp); PrintStack(&stack); - ReportSummary("SEGV", &stack); + Printf("AddressSanitizer can not provide additional info.\n"); + ReportErrorSummary("SEGV", &stack); } -void ReportDoubleFree(uptr addr, StackTrace *stack) { +void ReportDoubleFree(uptr addr, StackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); @@ -581,14 +575,15 @@ void ReportDoubleFree(uptr addr, StackTrace *stack) { "thread T%d%s:\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); - Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("double-free", stack); + ReportErrorSummary("double-free", &stack); } -void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { +void ReportFreeNotMalloced(uptr addr, StackTrace *free_stack) { ScopedInErrorReport in_report; Decorator d; Printf("%s", d.Warning()); @@ -598,12 +593,14 @@ void ReportFreeNotMalloced(uptr addr, StackTrace *stack) { "which was not malloc()-ed: %p in thread T%d%s\n", addr, curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname))); Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-free", stack); + ReportErrorSummary("bad-free", &stack); } -void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, +void ReportAllocTypeMismatch(uptr addr, StackTrace *free_stack, AllocType alloc_type, AllocType dealloc_type) { static const char *alloc_names[] = @@ -617,9 +614,11 @@ void ReportAllocTypeMismatch(uptr addr, StackTrace *stack, Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n", alloc_names[alloc_type], dealloc_names[dealloc_type], addr); Printf("%s", d.EndWarning()); - PrintStack(stack); + CHECK_GT(free_stack->size, 0); + GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp); + PrintStack(&stack); DescribeHeapAddress(addr, 1); - ReportSummary("alloc-dealloc-mismatch", stack); + ReportErrorSummary("alloc-dealloc-mismatch", &stack); Report("HINT: if you don't care about these warnings you may set " "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n"); } @@ -634,7 +633,7 @@ void ReportMallocUsableSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-malloc_usable_size", stack); + ReportErrorSummary("bad-malloc_usable_size", stack); } void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { @@ -647,7 +646,7 @@ void ReportAsanGetAllocatedSizeNotOwned(uptr addr, StackTrace *stack) { Printf("%s", d.EndWarning()); PrintStack(stack); DescribeHeapAddress(addr, 1); - ReportSummary("bad-__asan_get_allocated_size", stack); + ReportErrorSummary("bad-__asan_get_allocated_size", stack); } void ReportStringFunctionMemoryRangesOverlap( @@ -665,7 +664,7 @@ void ReportStringFunctionMemoryRangesOverlap( PrintStack(stack); DescribeAddress((uptr)offset1, length1); DescribeAddress((uptr)offset2, length2); - ReportSummary(bug_type, stack); + ReportErrorSummary(bug_type, stack); } // ----------------------- Mac-specific reports ----------------- {{{1 @@ -747,6 +746,9 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, case kAsanUserPoisonedMemoryMagic: bug_descr = "use-after-poison"; break; + case kAsanContiguousContainerOOBMagic: + bug_descr = "container-overflow"; + break; case kAsanStackUseAfterScopeMagic: bug_descr = "stack-use-after-scope"; break; @@ -775,7 +777,7 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, PrintStack(&stack); DescribeAddress(addr, access_size); - ReportSummary(bug_descr, &stack); + ReportErrorSummary(bug_descr, &stack); PrintShadowMemoryForAddress(addr); } diff --git a/libsanitizer/asan/asan_report.h b/libsanitizer/asan/asan_report.h index afe7673..e4c756e 100644 --- a/libsanitizer/asan/asan_report.h +++ b/libsanitizer/asan/asan_report.h @@ -31,9 +31,9 @@ void DescribeThread(AsanThreadContext *context); // Different kinds of error reports. void NORETURN ReportSIGSEGV(uptr pc, uptr sp, uptr bp, uptr addr); -void NORETURN ReportDoubleFree(uptr addr, StackTrace *stack); -void NORETURN ReportFreeNotMalloced(uptr addr, StackTrace *stack); -void NORETURN ReportAllocTypeMismatch(uptr addr, StackTrace *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, AllocType alloc_type, AllocType dealloc_type); void NORETURN ReportMallocUsableSizeNotOwned(uptr addr, diff --git a/libsanitizer/asan/asan_rtl.cc b/libsanitizer/asan/asan_rtl.cc index 6732761..537d406 100644 --- a/libsanitizer/asan/asan_rtl.cc +++ b/libsanitizer/asan/asan_rtl.cc @@ -49,6 +49,8 @@ static void AsanDie() { UnmapOrDie((void*)kLowShadowBeg, kHighShadowEnd - kLowShadowBeg); } } + if (flags()->coverage) + __sanitizer_cov_dump(); if (death_callback) death_callback(); if (flags()->abort_on_error) @@ -86,11 +88,11 @@ static const char *MaybeUseAsanDefaultOptionsCompileDefiniton() { } static void ParseFlagsFromString(Flags *f, const char *str) { - ParseCommonFlagsFromString(str); - CHECK((uptr)common_flags()->malloc_context_size <= kStackTraceMax); + CommonFlags *cf = common_flags(); + ParseCommonFlagsFromString(cf, str); + CHECK((uptr)cf->malloc_context_size <= kStackTraceMax); ParseFlag(str, &f->quarantine_size, "quarantine_size"); - ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->redzone, "redzone"); CHECK_GE(f->redzone, 16); CHECK(IsPowerOfTwo(f->redzone)); @@ -119,32 +121,25 @@ static void ParseFlagsFromString(Flags *f, const char *str) { ParseFlag(str, &f->print_stats, "print_stats"); ParseFlag(str, &f->print_legend, "print_legend"); ParseFlag(str, &f->atexit, "atexit"); + ParseFlag(str, &f->coverage, "coverage"); ParseFlag(str, &f->disable_core, "disable_core"); ParseFlag(str, &f->allow_reexec, "allow_reexec"); ParseFlag(str, &f->print_full_thread_history, "print_full_thread_history"); ParseFlag(str, &f->poison_heap, "poison_heap"); + ParseFlag(str, &f->poison_partial, "poison_partial"); ParseFlag(str, &f->alloc_dealloc_mismatch, "alloc_dealloc_mismatch"); - ParseFlag(str, &f->use_stack_depot, "use_stack_depot"); ParseFlag(str, &f->strict_memcmp, "strict_memcmp"); ParseFlag(str, &f->strict_init_order, "strict_init_order"); } void InitializeFlags(Flags *f, const char *env) { CommonFlags *cf = common_flags(); + SetCommonFlagsDefaults(cf); cf->external_symbolizer_path = GetEnv("ASAN_SYMBOLIZER_PATH"); - cf->symbolize = true; cf->malloc_context_size = kDefaultMallocContextSize; - cf->fast_unwind_on_fatal = false; - cf->fast_unwind_on_malloc = true; - cf->strip_path_prefix = ""; - cf->handle_ioctl = false; - cf->log_path = 0; - cf->detect_leaks = false; - cf->leak_check_at_exit = true; internal_memset(f, 0, sizeof(*f)); f->quarantine_size = (ASAN_LOW_MEMORY) ? 1UL << 26 : 1UL << 28; - f->verbosity = 0; f->redzone = 16; f->debug = false; f->report_globals = 1; @@ -168,14 +163,15 @@ void InitializeFlags(Flags *f, const char *env) { f->print_stats = false; f->print_legend = true; f->atexit = false; + f->coverage = false; f->disable_core = (SANITIZER_WORDSIZE == 64); f->allow_reexec = true; f->print_full_thread_history = true; f->poison_heap = true; + f->poison_partial = true; // Turn off alloc/dealloc mismatch checker on Mac and Windows for now. // TODO(glider,timurrrr): Fix known issues and enable this back. f->alloc_dealloc_mismatch = (SANITIZER_MAC == 0) && (SANITIZER_WINDOWS == 0); - f->use_stack_depot = true; f->strict_memcmp = true; f->strict_init_order = false; @@ -184,7 +180,7 @@ void InitializeFlags(Flags *f, const char *env) { // Override from user-specified string. ParseFlagsFromString(f, MaybeCallAsanDefaultOptions()); - if (flags()->verbosity) { + if (cf->verbosity) { Report("Using the defaults from __asan_default_options: %s\n", MaybeCallAsanDefaultOptions()); } @@ -200,10 +196,10 @@ void InitializeFlags(Flags *f, const char *env) { } #endif - if (cf->detect_leaks && !f->use_stack_depot) { - Report("%s: detect_leaks is ignored (requires use_stack_depot).\n", - SanitizerToolName); - cf->detect_leaks = false; + // Make "strict_init_order" imply "check_initialization_order". + // TODO(samsonov): Use a single runtime flag for an init-order checker. + if (f->strict_init_order) { + f->check_initialization_order = true; } } @@ -462,7 +458,7 @@ void __asan_init() { __asan_option_detect_stack_use_after_return = flags()->detect_stack_use_after_return; - if (flags()->verbosity && options) { + if (common_flags()->verbosity && options) { Report("Parsed ASAN_OPTIONS: %s\n", options); } @@ -472,11 +468,6 @@ void __asan_init() { // Setup internal allocator callback. SetLowLevelAllocateCallback(OnLowLevelAllocate); - if (flags()->atexit) { - Atexit(asan_atexit); - } - - // interceptors InitializeAsanInterceptors(); ReplaceSystemMalloc(); @@ -495,7 +486,7 @@ void __asan_init() { } #endif - if (flags()->verbosity) + if (common_flags()->verbosity) PrintAddressSpaceLayout(); if (flags()->disable_core) { @@ -531,17 +522,18 @@ void __asan_init() { Die(); } + AsanTSDInit(PlatformTSDDtor); InstallSignalHandlers(); - AsanTSDInit(AsanThread::TSDDtor); // Allocator should be initialized before starting external symbolizer, as // fork() on Mac locks the allocator. InitializeAllocator(); // Start symbolizer process if necessary. - if (common_flags()->symbolize && &getSymbolizer) { - getSymbolizer() - ->InitializeExternal(common_flags()->external_symbolizer_path); + if (common_flags()->symbolize) { + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); } // On Linux AsanThread::ThreadStart() calls malloc() that's why asan_inited @@ -549,6 +541,13 @@ void __asan_init() { asan_inited = 1; asan_init_is_running = false; + if (flags()->atexit) + Atexit(asan_atexit); + + if (flags()->coverage) + Atexit(__sanitizer_cov_dump); + + // interceptors InitTlsSize(); // Create main thread. @@ -568,7 +567,7 @@ void __asan_init() { } #endif // CAN_SANITIZE_LEAKS - if (flags()->verbosity) { + if (common_flags()->verbosity) { Report("AddressSanitizer Init done\n"); } } diff --git a/libsanitizer/asan/asan_stack.cc b/libsanitizer/asan/asan_stack.cc index 7495251..93671a0 100644 --- a/libsanitizer/asan/asan_stack.cc +++ b/libsanitizer/asan/asan_stack.cc @@ -22,9 +22,12 @@ static bool MaybeCallAsanSymbolize(const void *pc, char *out_buffer, : false; } +void PrintStack(const uptr *trace, uptr size) { + StackTrace::PrintStack(trace, size, MaybeCallAsanSymbolize); +} + void PrintStack(StackTrace *stack) { - stack->PrintStack(stack->trace, stack->size, common_flags()->symbolize, - common_flags()->strip_path_prefix, MaybeCallAsanSymbolize); + PrintStack(stack->trace, stack->size); } } // namespace __asan diff --git a/libsanitizer/asan/asan_stack.h b/libsanitizer/asan/asan_stack.h index 3c0ac31..c929c88 100644 --- a/libsanitizer/asan/asan_stack.h +++ b/libsanitizer/asan/asan_stack.h @@ -20,6 +20,7 @@ namespace __asan { void PrintStack(StackTrace *stack); +void PrintStack(const uptr *trace, uptr size); } // namespace __asan @@ -29,19 +30,24 @@ void PrintStack(StackTrace *stack); #if SANITIZER_WINDOWS #define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ StackTrace stack; \ - GetStackTrace(&stack, max_s, pc, bp, 0, 0, fast) + stack.Unwind(max_s, pc, bp, 0, 0, fast) #else -#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ - StackTrace stack; \ - { \ - AsanThread *t; \ - stack.size = 0; \ - if (asan_inited && (t = GetCurrentThread()) && !t->isUnwinding()) { \ - uptr stack_top = t->stack_top(); \ - uptr stack_bottom = t->stack_bottom(); \ - ScopedUnwinding unwind_scope(t); \ - GetStackTrace(&stack, max_s, pc, bp, stack_top, stack_bottom, fast); \ - } \ +#define GET_STACK_TRACE_WITH_PC_AND_BP(max_s, pc, bp, fast) \ + StackTrace stack; \ + { \ + AsanThread *t; \ + stack.size = 0; \ + if (asan_inited) { \ + if ((t = GetCurrentThread()) && !t->isUnwinding()) { \ + uptr stack_top = t->stack_top(); \ + uptr stack_bottom = t->stack_bottom(); \ + ScopedUnwinding unwind_scope(t); \ + stack.Unwind(max_s, pc, bp, stack_top, stack_bottom, fast); \ + } else if (t == 0 && !fast) { \ + /* If GetCurrentThread() has failed, try to do slow unwind anyways. */ \ + stack.Unwind(max_s, pc, bp, 0, 0, false); \ + } \ + } \ } #endif // SANITIZER_WINDOWS diff --git a/libsanitizer/asan/asan_stats.h b/libsanitizer/asan/asan_stats.h index 2f964f8..5b04b17 100644 --- a/libsanitizer/asan/asan_stats.h +++ b/libsanitizer/asan/asan_stats.h @@ -45,9 +45,9 @@ struct AsanStats { uptr malloc_large; uptr malloc_small_slow; - // Ctor for global AsanStats (accumulated stats and main thread stats). + // Ctor for global AsanStats (accumulated stats for dead threads). explicit AsanStats(LinkerInitialized) { } - // Default ctor for thread-local stats. + // Creates empty stats. AsanStats(); void Print(); // Prints formatted stats to stderr. diff --git a/libsanitizer/asan/asan_thread.cc b/libsanitizer/asan/asan_thread.cc index 1da714c..5a9c2dd 100644 --- a/libsanitizer/asan/asan_thread.cc +++ b/libsanitizer/asan/asan_thread.cc @@ -17,6 +17,7 @@ #include "asan_mapping.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "lsan/lsan_common.h" namespace __asan { @@ -25,9 +26,8 @@ namespace __asan { void AsanThreadContext::OnCreated(void *arg) { CreateThreadContextArgs *args = static_cast(arg); - if (args->stack) { - internal_memcpy(&stack, args->stack, sizeof(stack)); - } + if (args->stack) + stack_id = StackDepotPut(args->stack->trace, args->stack->size); thread = args->thread; thread->set_context(this); } @@ -41,9 +41,12 @@ void AsanThreadContext::OnFinished() { static ALIGNED(16) char thread_registry_placeholder[sizeof(ThreadRegistry)]; static ThreadRegistry *asan_thread_registry; +static BlockingMutex mu_for_thread_context(LINKER_INITIALIZED); +static LowLevelAllocator allocator_for_thread_context; + static ThreadContextBase *GetAsanThreadContext(u32 tid) { - void *mem = MmapOrDie(sizeof(AsanThreadContext), "AsanThreadContext"); - return new(mem) AsanThreadContext(tid); + BlockingMutexLock lock(&mu_for_thread_context); + return new(allocator_for_thread_context) AsanThreadContext(tid); } ThreadRegistry &asanThreadRegistry() { @@ -76,24 +79,25 @@ AsanThread *AsanThread::Create(thread_callback_t start_routine, AsanThread *thread = (AsanThread*)MmapOrDie(size, __FUNCTION__); thread->start_routine_ = start_routine; thread->arg_ = arg; - thread->context_ = 0; return thread; } void AsanThread::TSDDtor(void *tsd) { AsanThreadContext *context = (AsanThreadContext*)tsd; - if (flags()->verbosity >= 1) + if (common_flags()->verbosity >= 1) Report("T%d TSDDtor\n", context->tid); if (context->thread) context->thread->Destroy(); } void AsanThread::Destroy() { - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { Report("T%d exited\n", tid()); } + malloc_storage().CommitBack(); + if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); asanThreadRegistry().FinishThread(tid()); FlushToDeadThreadStats(&stats_); // We also clear the shadow on thread destruction because @@ -136,7 +140,7 @@ void AsanThread::Init() { CHECK(AddrIsInMem(stack_bottom_)); CHECK(AddrIsInMem(stack_top_ - 1)); ClearShadowForThreadStackAndTLS(); - if (flags()->verbosity >= 1) { + if (common_flags()->verbosity >= 1) { int local = 0; Report("T%d: stack [%p,%p) size 0x%zx; local=%p\n", tid(), (void*)stack_bottom_, (void*)stack_top_, @@ -160,10 +164,14 @@ thread_return_t AsanThread::ThreadStart(uptr os_id) { } thread_return_t res = start_routine_(arg_); - malloc_storage().CommitBack(); - if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); - this->Destroy(); + // On POSIX systems we defer this to the TSD destructor. LSan will consider + // the thread's memory as non-live from the moment we call Destroy(), even + // though that memory might contain pointers to heap objects which will be + // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before + // the TSD destructors have run might cause false positives in LSan. + if (!SANITIZER_POSIX) + this->Destroy(); return res; } @@ -257,7 +265,7 @@ AsanThread *GetCurrentThread() { void SetCurrentThread(AsanThread *t) { CHECK(t->context()); - if (flags()->verbosity >= 2) { + if (common_flags()->verbosity >= 2) { Report("SetCurrentThread: %p for thread %p\n", t->context(), (void*)GetThreadSelf()); } @@ -286,6 +294,13 @@ void EnsureMainThreadIDIsCorrect() { if (context && (context->tid == 0)) context->os_id = GetTid(); } + +__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) { + __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( + __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); + if (!context) return 0; + return context->thread; +} } // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 @@ -293,10 +308,7 @@ namespace __lsan { bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end) { - __asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>( - __asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id)); - if (!context) return false; - __asan::AsanThread *t = context->thread; + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); if (!t) return false; *stack_begin = t->stack_bottom(); *stack_end = t->stack_top(); @@ -308,6 +320,13 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, return true; } +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { + __asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id); + if (t && t->has_fake_stack()) + t->fake_stack()->ForEachFakeFrame(callback, arg); +} + void LockThreadRegistry() { __asan::asanThreadRegistry().Lock(); } diff --git a/libsanitizer/asan/asan_thread.h b/libsanitizer/asan/asan_thread.h index f21971f..5a917fa 100644 --- a/libsanitizer/asan/asan_thread.h +++ b/libsanitizer/asan/asan_thread.h @@ -17,6 +17,7 @@ #include "asan_fake_stack.h" #include "asan_stack.h" #include "asan_stats.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_thread_registry.h" @@ -34,11 +35,13 @@ class AsanThreadContext : public ThreadContextBase { explicit AsanThreadContext(int tid) : ThreadContextBase(tid), announced(false), + destructor_iterations(kPthreadDestructorIterations), + stack_id(0), thread(0) { - internal_memset(&stack, 0, sizeof(stack)); } bool announced; - StackTrace stack; + u8 destructor_iterations; + u32 stack_id; AsanThread *thread; void OnCreated(void *arg); @@ -46,7 +49,7 @@ class AsanThreadContext : public ThreadContextBase { }; // AsanThreadContext objects are never freed, so we need many of them. -COMPILER_CHECK(sizeof(AsanThreadContext) <= 4096); +COMPILER_CHECK(sizeof(AsanThreadContext) <= 256); // AsanThread are stored in TSD and destroyed when the thread dies. class AsanThread { @@ -96,14 +99,15 @@ class AsanThread { // True is this thread is currently unwinding stack (i.e. collecting a stack // trace). Used to prevent deadlocks on platforms where libc unwinder calls // malloc internally. See PR17116 for more details. - bool isUnwinding() const { return unwinding; } - void setUnwinding(bool b) { unwinding = b; } + bool isUnwinding() const { return unwinding_; } + void setUnwinding(bool b) { unwinding_ = b; } AsanThreadLocalMallocStorage &malloc_storage() { return malloc_storage_; } AsanStats &stats() { return stats_; } private: - AsanThread() : unwinding(false) {} + // NOTE: There is no AsanThread constructor. It is allocated + // via mmap() and *must* be valid in zero-initialized state. void SetThreadStackAndTls(); void ClearShadowForThreadStackAndTLS(); FakeStack *AsyncSignalSafeLazyInitFakeStack(); @@ -111,18 +115,18 @@ class AsanThread { AsanThreadContext *context_; thread_callback_t start_routine_; void *arg_; - uptr stack_top_; - uptr stack_bottom_; + uptr stack_top_; + uptr stack_bottom_; // stack_size_ == stack_top_ - stack_bottom_; // It needs to be set in a async-signal-safe manner. - uptr stack_size_; + uptr stack_size_; uptr tls_begin_; uptr tls_end_; FakeStack *fake_stack_; AsanThreadLocalMallocStorage malloc_storage_; AsanStats stats_; - bool unwinding; + bool unwinding_; }; // ScopedUnwinding is a scope for stacktracing member of a context diff --git a/libsanitizer/asan/asan_win.cc b/libsanitizer/asan/asan_win.cc index ed785b6..8ffa58f 100644 --- a/libsanitizer/asan/asan_win.cc +++ b/libsanitizer/asan/asan_win.cc @@ -58,6 +58,9 @@ void AsanTSDSet(void *tsd) { fake_tsd = tsd; } +void PlatformTSDDtor(void *tsd) { + AsanThread::TSDDtor(tsd); +} // ---------------------- Various stuff ---------------- {{{1 void MaybeReexec() { // No need to re-exec on Windows. diff --git a/libsanitizer/include/sanitizer/common_interface_defs.h b/libsanitizer/include/sanitizer/common_interface_defs.h index a7db2ff..956cb86 100644 --- a/libsanitizer/include/sanitizer/common_interface_defs.h +++ b/libsanitizer/include/sanitizer/common_interface_defs.h @@ -25,10 +25,6 @@ extern "C" { // Tell the tools to write their reports to "path." instead of stderr. void __sanitizer_set_report_path(const char *path); - // Tell the tools to write their reports to given file descriptor instead of - // stderr. - void __sanitizer_set_report_fd(int fd); - // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. @@ -49,6 +45,44 @@ extern "C" { void __sanitizer_unaligned_store32(void *p, uint32_t x); void __sanitizer_unaligned_store64(void *p, uint64_t x); + // Record and dump coverage info. + void __sanitizer_cov_dump(); + + // Annotate the current state of a contiguous container, such as + // std::vector, std::string or similar. + // A contiguous container is a container that keeps all of its elements + // in a contiguous region of memory. The container owns the region of memory + // [beg, end); the memory [beg, mid) is used to store the current elements + // and the memory [mid, end) is reserved for future elements; + // end <= mid <= end. For example, in "std::vector<> v" + // beg = &v[0]; + // end = beg + v.capacity() * sizeof(v[0]); + // mid = beg + v.size() * sizeof(v[0]); + // + // This annotation tells the Sanitizer tool about the current state of the + // container so that the tool can report errors when memory from [mid, end) + // is accessed. Insert this annotation into methods like push_back/pop_back. + // Supply the old and the new values of mid (old_mid/new_mid). + // In the initial state mid == end and so should be the final + // state when the container is destroyed or when it reallocates the storage. + // + // Use with caution and don't use for anything other than vector-like classes. + // + // For AddressSanitizer, 'beg' should be 8-aligned and 'end' should + // be either 8-aligned or it should point to the end of a separate heap-, + // stack-, or global- allocated buffer. I.e. the following will not work: + // int64_t x[2]; // 16 bytes, 8-aligned. + // char *beg = (char *)&x[0]; + // char *end = beg + 12; // Not 8 aligned, not the end of the buffer. + // This however will work fine: + // int32_t x[3]; // 12 bytes, but 8-aligned under AddressSanitizer. + // char *beg = (char*)&x[0]; + // char *end = beg + 12; // Not 8-aligned, but is the end of the buffer. + void __sanitizer_annotate_contiguous_container(const void *beg, + const void *end, + const void *old_mid, + const void *new_mid); + #ifdef __cplusplus } // extern "C" #endif diff --git a/libsanitizer/include/sanitizer/linux_syscall_hooks.h b/libsanitizer/include/sanitizer/linux_syscall_hooks.h index 6cd7d76..17742b6 100644 --- a/libsanitizer/include/sanitizer/linux_syscall_hooks.h +++ b/libsanitizer/include/sanitizer/linux_syscall_hooks.h @@ -985,10 +985,11 @@ #else #define __sanitizer_syscall_pre_pread64(fd, buf, count, pos0, pos1) \ __sanitizer_syscall_pre_impl_pread64((long)(fd), (long)(buf), (long)(count), \ - (long)(pos)) + (long)(pos0), (long)(pos1)) #define __sanitizer_syscall_post_pread64(res, fd, buf, count, pos0, pos1) \ __sanitizer_syscall_post_impl_pread64(res, (long)(fd), (long)(buf), \ - (long)(count), (long)(pos)) + (long)(count), (long)(pos0), \ + (long)(pos1)) #define __sanitizer_syscall_pre_pwrite64(fd, buf, count, pos0, pos1) \ __sanitizer_syscall_pre_impl_pwrite64( \ (long)(fd), (long)(buf), (long)(count), (long)(pos0), (long)(pos1)) diff --git a/libsanitizer/interception/interception.h b/libsanitizer/interception/interception.h index 71740c5..7393f4c 100644 --- a/libsanitizer/interception/interception.h +++ b/libsanitizer/interception/interception.h @@ -236,12 +236,18 @@ typedef unsigned long uptr; // NOLINT #if defined(__linux__) # include "interception_linux.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_LINUX(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_LINUX(func, symver) #elif defined(__APPLE__) # include "interception_mac.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_MAC(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_MAC(func, symver) #else // defined(_WIN32) # include "interception_win.h" # define INTERCEPT_FUNCTION(func) INTERCEPT_FUNCTION_WIN(func) +# define INTERCEPT_FUNCTION_VER(func, symver) \ + INTERCEPT_FUNCTION_VER_WIN(func, symver) #endif #undef INCLUDED_FROM_INTERCEPTION_LIB diff --git a/libsanitizer/interception/interception_linux.h b/libsanitizer/interception/interception_linux.h index fbbfecb..5ab24db 100644 --- a/libsanitizer/interception/interception_linux.h +++ b/libsanitizer/interception/interception_linux.h @@ -33,9 +33,12 @@ void *GetFuncAddrVer(const char *func_name, const char *ver); (::__interception::uptr)&WRAP(func)) #if !defined(__ANDROID__) // android does not have dlvsym -#define INTERCEPT_FUNCTION_VER(func, symver) \ - ::__interception::real_##func = (func##_f)(unsigned long) \ - ::__interception::GetFuncAddrVer(#func, #symver) +# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \ + ::__interception::real_##func = (func##_f)(unsigned long) \ + ::__interception::GetFuncAddrVer(#func, symver) +#else +# define INTERCEPT_FUNCTION_VER_LINUX(func, symver) \ + INTERCEPT_FUNCTION_LINUX(func) #endif // !defined(__ANDROID__) #endif // INTERCEPTION_LINUX_H diff --git a/libsanitizer/interception/interception_mac.h b/libsanitizer/interception/interception_mac.h index 1b11182..fbcb473 100644 --- a/libsanitizer/interception/interception_mac.h +++ b/libsanitizer/interception/interception_mac.h @@ -20,6 +20,7 @@ #define INTERCEPTION_MAC_H #define INTERCEPT_FUNCTION_MAC(func) +#define INTERCEPT_FUNCTION_VER_MAC(func, symver) #endif // INTERCEPTION_MAC_H #endif // __APPLE__ diff --git a/libsanitizer/interception/interception_win.h b/libsanitizer/interception/interception_win.h index ebac168..b46ad0d 100644 --- a/libsanitizer/interception/interception_win.h +++ b/libsanitizer/interception/interception_win.h @@ -39,5 +39,8 @@ bool OverrideFunction(uptr old_func, uptr new_func, uptr *orig_old_func); (::__interception::uptr*)&REAL(func)) #endif +#define INTERCEPT_FUNCTION_VER_WIN(func, symver) \ + INTERCEPT_FUNCTION_WIN(func) + #endif // INTERCEPTION_WIN_H #endif // _WIN32 diff --git a/libsanitizer/lsan/Makefile.am b/libsanitizer/lsan/Makefile.am index 36fd605..4784d7c 100644 --- a/libsanitizer/lsan/Makefile.am +++ b/libsanitizer/lsan/Makefile.am @@ -22,6 +22,7 @@ lsan_files = \ lsan.cc \ lsan_allocator.cc \ lsan_interceptors.cc \ + lsan_preinit.cc \ lsan_thread.cc libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files) diff --git a/libsanitizer/lsan/Makefile.in b/libsanitizer/lsan/Makefile.in index 9296b70..b09469e 100644 --- a/libsanitizer/lsan/Makefile.in +++ b/libsanitizer/lsan/Makefile.in @@ -83,7 +83,7 @@ liblsan_la_DEPENDENCIES = \ $(am__DEPENDENCIES_1) am__objects_1 = lsan_common.lo lsan_common_linux.lo am__objects_2 = $(am__objects_1) lsan.lo lsan_allocator.lo \ - lsan_interceptors.lo lsan_thread.lo + lsan_interceptors.lo lsan_preinit.lo lsan_thread.lo am_liblsan_la_OBJECTS = $(am__objects_2) liblsan_la_OBJECTS = $(am_liblsan_la_OBJECTS) liblsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ @@ -264,6 +264,7 @@ lsan_files = \ lsan.cc \ lsan_allocator.cc \ lsan_interceptors.cc \ + lsan_preinit.cc \ lsan_thread.cc libsanitizer_lsan_la_SOURCES = $(sanitizer_lsan_files) @@ -400,6 +401,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_common_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_interceptors.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_preinit.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/lsan_thread.Plo@am__quote@ .cc.o: diff --git a/libsanitizer/lsan/lsan.cc b/libsanitizer/lsan/lsan.cc index 500da50..270979a 100644 --- a/libsanitizer/lsan/lsan.cc +++ b/libsanitizer/lsan/lsan.cc @@ -18,26 +18,30 @@ #include "lsan_common.h" #include "lsan_thread.h" +bool lsan_inited; +bool lsan_init_is_running; + namespace __lsan { static void InitializeCommonFlags() { CommonFlags *cf = common_flags(); + SetCommonFlagsDefaults(cf); cf->external_symbolizer_path = GetEnv("LSAN_SYMBOLIZER_PATH"); - cf->symbolize = true; - cf->strip_path_prefix = ""; - cf->fast_unwind_on_malloc = true; cf->malloc_context_size = 30; cf->detect_leaks = true; - cf->leak_check_at_exit = true; - ParseCommonFlagsFromString(GetEnv("LSAN_OPTIONS")); + ParseCommonFlagsFromString(cf, GetEnv("LSAN_OPTIONS")); } -void Init() { - static bool inited; - if (inited) +} // namespace __lsan + +using namespace __lsan; // NOLINT + +extern "C" void __lsan_init() { + CHECK(!lsan_init_is_running); + if (lsan_inited) return; - inited = true; + lsan_init_is_running = true; SanitizerToolName = "LeakSanitizer"; InitializeCommonFlags(); InitializeAllocator(); @@ -51,13 +55,14 @@ void Init() { // Start symbolizer process if necessary. if (common_flags()->symbolize) { - getSymbolizer() - ->InitializeExternal(common_flags()->external_symbolizer_path); + Symbolizer::Init(common_flags()->external_symbolizer_path); + } else { + Symbolizer::Disable(); } InitCommonLsan(); if (common_flags()->detect_leaks && common_flags()->leak_check_at_exit) Atexit(DoLeakCheck); + lsan_inited = true; + lsan_init_is_running = false; } - -} // namespace __lsan diff --git a/libsanitizer/lsan/lsan.h b/libsanitizer/lsan/lsan.h index 18ff5da..8a5030c 100644 --- a/libsanitizer/lsan/lsan.h +++ b/libsanitizer/lsan/lsan.h @@ -15,7 +15,11 @@ namespace __lsan { -void Init(); void InitializeInterceptors(); } // namespace __lsan + +extern bool lsan_inited; +extern bool lsan_init_is_running; + +extern "C" void __lsan_init(); diff --git a/libsanitizer/lsan/lsan_allocator.cc b/libsanitizer/lsan/lsan_allocator.cc index 66af603..ce47dfc 100644 --- a/libsanitizer/lsan/lsan_allocator.cc +++ b/libsanitizer/lsan/lsan_allocator.cc @@ -18,6 +18,8 @@ #include "sanitizer_common/sanitizer_stacktrace.h" #include "lsan_common.h" +extern "C" void *memset(void *ptr, int value, uptr num); + namespace __lsan { static const uptr kMaxAllowedMallocSize = 8UL << 30; @@ -32,7 +34,7 @@ struct ChunkMetadata { }; typedef SizeClassAllocator64 PrimaryAllocator; + sizeof(ChunkMetadata), DefaultSizeClassMap> PrimaryAllocator; typedef SizeClassAllocatorLocalCache AllocatorCache; typedef LargeMmapAllocator<> SecondaryAllocator; typedef CombinedAllocatordetect_leaks) { + // Initialization which can fail or print warnings should only be done if + // LSan is actually enabled. + InitializeSuppressions(); + InitializePlatformSpecificModules(); + } } class Decorator: private __sanitizer::AnsiColorDecorator { @@ -136,6 +140,8 @@ void ScanRangeForPointers(uptr begin, uptr end, if (!CanBeAHeapPointer(reinterpret_cast(p))) continue; uptr chunk = PointsIntoChunk(p); if (!chunk) continue; + // Pointers to self don't count. This matters when tag == kIndirectlyLeaked. + if (chunk == begin) continue; LsanMetadata m(chunk); // Reachable beats ignored beats leaked. if (m.tag() == kReachable) continue; @@ -149,6 +155,11 @@ void ScanRangeForPointers(uptr begin, uptr end, } } +void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) { + Frontier *frontier = reinterpret_cast(arg); + ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable); +} + // Scans thread data (stacks and TLS) for heap pointers. static void ProcessThreads(SuspendedThreadsList const &suspended_threads, Frontier *frontier) { @@ -197,6 +208,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads, } ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK", kReachable); + ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier); } if (flags()->use_tls) { @@ -261,6 +273,8 @@ static void ClassifyAllChunks(SuspendedThreadsList const &suspended_threads) { // The check here is relatively expensive, so we do this in a separate flood // fill. That way we can skip the check for chunks that are reachable // otherwise. + if (flags()->log_pointers) + Report("Processing platform-specific allocations.\n"); ProcessPlatformSpecificAllocations(&frontier); FloodFillTag(&frontier, kReachable); @@ -281,8 +295,7 @@ 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, common_flags()->symbolize, - common_flags()->strip_path_prefix, 0); + StackTrace::PrintStack(trace, size); } // ForEachChunk callback. Aggregates unreachable chunks into a LeakReport. @@ -400,8 +413,8 @@ static Suppression *GetSuppressionForAddr(uptr addr) { static const uptr kMaxAddrFrames = 16; InternalScopedBuffer addr_frames(kMaxAddrFrames); for (uptr i = 0; i < kMaxAddrFrames; i++) new (&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = - getSymbolizer()->SymbolizeCode(addr, addr_frames.data(), kMaxAddrFrames); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); for (uptr i = 0; i < addr_frames_num; i++) { Suppression* s; if (suppression_ctx->Match(addr_frames[i].function, SuppressionLeak, &s) || @@ -479,7 +492,6 @@ void LeakReport::PrintLargest(uptr num_leaks_to_print) { leaks_[i].total_size, leaks_[i].hit_count); Printf("%s", d.End()); PrintStackTraceById(leaks_[i].stack_trace_id); - Printf("\n"); leaks_printed++; if (leaks_printed == num_leaks_to_print) break; } @@ -497,12 +509,11 @@ void LeakReport::PrintSummary() { bytes += leaks_[i].total_size; allocations += leaks_[i].hit_count; } - const int kMaxSummaryLength = 128; InternalScopedBuffer summary(kMaxSummaryLength); - internal_snprintf(summary.data(), kMaxSummaryLength, - "LeakSanitizer: %zu byte(s) leaked in %zu allocation(s).", - bytes, allocations); - __sanitizer_report_error_summary(summary.data()); + internal_snprintf(summary.data(), summary.size(), + "%zu byte(s) leaked in %zu allocation(s).", bytes, + allocations); + ReportErrorSummary(summary.data()); } uptr LeakReport::ApplySuppressions() { @@ -528,6 +539,8 @@ extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __lsan_ignore_object(const void *p) { #if CAN_SANITIZE_LEAKS + if (!common_flags()->detect_leaks) + return; // Cannot use PointsIntoChunk or LsanMetadata here, since the allocator is not // locked. BlockingMutexLock l(&global_mutex); @@ -552,7 +565,7 @@ void __lsan_disable() { SANITIZER_INTERFACE_ATTRIBUTE void __lsan_enable() { #if CAN_SANITIZE_LEAKS - if (!__lsan::disable_counter) { + if (!__lsan::disable_counter && common_flags()->detect_leaks) { Report("Unmatched call to __lsan_enable().\n"); Die(); } diff --git a/libsanitizer/lsan/lsan_common.h b/libsanitizer/lsan/lsan_common.h index 7906ecb..5d9b4eb 100644 --- a/libsanitizer/lsan/lsan_common.h +++ b/libsanitizer/lsan/lsan_common.h @@ -133,6 +133,8 @@ void UnlockThreadRegistry(); bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end); +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg); // If called from the main thread, updates the main thread's TID in the thread // registry. We need this to handle processes that fork() without a subsequent // exec(), which invalidates the recorded TID. To update it, we must call diff --git a/libsanitizer/lsan/lsan_interceptors.cc b/libsanitizer/lsan/lsan_interceptors.cc index 40ddc77..1940902 100644 --- a/libsanitizer/lsan/lsan_interceptors.cc +++ b/libsanitizer/lsan/lsan_interceptors.cc @@ -42,11 +42,17 @@ int pthread_setspecific(unsigned key, const void *v); stack_top = t->stack_end(); \ stack_bottom = t->stack_begin(); \ } \ - GetStackTrace(&stack, __sanitizer::common_flags()->malloc_context_size, \ - StackTrace::GetCurrentPc(), \ - GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ + stack.Unwind(__sanitizer::common_flags()->malloc_context_size, \ + StackTrace::GetCurrentPc(), \ + GET_CURRENT_FRAME(), stack_top, stack_bottom, fast); \ } +#define ENSURE_LSAN_INITED do { \ + CHECK(!lsan_init_is_running); \ + if (!lsan_inited) \ + __lsan_init(); \ +} while (0) + ///// Malloc/free interceptors. ///// const bool kAlwaysClearMemory = true; @@ -56,38 +62,49 @@ namespace std { } INTERCEPTOR(void*, malloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Allocate(stack, size, 1, kAlwaysClearMemory); } INTERCEPTOR(void, free, void *p) { - Init(); + ENSURE_LSAN_INITED; Deallocate(p); } INTERCEPTOR(void*, calloc, uptr nmemb, uptr size) { + if (lsan_init_is_running) { + // Hack: dlsym calls calloc before REAL(calloc) is retrieved from dlsym. + const uptr kCallocPoolSize = 1024; + static uptr calloc_memory_for_dlsym[kCallocPoolSize]; + static uptr allocated; + uptr size_in_words = ((nmemb * size) + kWordSize - 1) / kWordSize; + void *mem = (void*)&calloc_memory_for_dlsym[allocated]; + allocated += size_in_words; + CHECK(allocated < kCallocPoolSize); + return mem; + } if (CallocShouldReturnNullDueToOverflow(size, nmemb)) return 0; - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; size *= nmemb; return Allocate(stack, size, 1, true); } INTERCEPTOR(void*, realloc, void *q, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Reallocate(stack, q, size, 1); } INTERCEPTOR(void*, memalign, uptr alignment, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; return Allocate(stack, size, alignment, kAlwaysClearMemory); } INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; *memptr = Allocate(stack, size, alignment, kAlwaysClearMemory); // FIXME: Return ENOMEM if user requested more than max alloc size. @@ -95,7 +112,7 @@ INTERCEPTOR(int, posix_memalign, void **memptr, uptr alignment, uptr size) { } INTERCEPTOR(void*, valloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; if (size == 0) size = GetPageSizeCached(); @@ -103,7 +120,7 @@ INTERCEPTOR(void*, valloc, uptr size) { } INTERCEPTOR(uptr, malloc_usable_size, void *ptr) { - Init(); + ENSURE_LSAN_INITED; return GetMallocUsableSize(ptr); } @@ -122,7 +139,7 @@ INTERCEPTOR(int, mallopt, int cmd, int value) { } INTERCEPTOR(void*, pvalloc, uptr size) { - Init(); + ENSURE_LSAN_INITED; GET_STACK_TRACE; uptr PageSize = GetPageSizeCached(); size = RoundUpTo(size, PageSize); @@ -136,7 +153,7 @@ INTERCEPTOR(void*, pvalloc, uptr size) { INTERCEPTOR(void, cfree, void *p) ALIAS("free"); #define OPERATOR_NEW_BODY \ - Init(); \ + ENSURE_LSAN_INITED; \ GET_STACK_TRACE; \ return Allocate(stack, size, 1, kAlwaysClearMemory); @@ -150,7 +167,7 @@ INTERCEPTOR_ATTRIBUTE void *operator new[](uptr size, std::nothrow_t const&) { OPERATOR_NEW_BODY; } #define OPERATOR_DELETE_BODY \ - Init(); \ + ENSURE_LSAN_INITED; \ Deallocate(ptr); INTERCEPTOR_ATTRIBUTE @@ -190,9 +207,6 @@ struct ThreadParam { atomic_uintptr_t tid; }; -// PTHREAD_DESTRUCTOR_ITERATIONS from glibc. -const uptr kPthreadDestructorIterations = 4; - extern "C" void *__lsan_thread_start_func(void *arg) { ThreadParam *p = (ThreadParam*)arg; void* (*callback)(void *arg) = p->callback; @@ -215,14 +229,14 @@ extern "C" void *__lsan_thread_start_func(void *arg) { INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void *), void *param) { - Init(); + ENSURE_LSAN_INITED; EnsureMainThreadIDIsCorrect(); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } - AdjustStackSizeLinux(attr, 0); + AdjustStackSizeLinux(attr); int detached = 0; pthread_attr_getdetachstate(attr, &detached); ThreadParam p; @@ -243,7 +257,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, } INTERCEPTOR(int, pthread_join, void *th, void **ret) { - Init(); + ENSURE_LSAN_INITED; int tid = ThreadTid((uptr)th); int res = REAL(pthread_join)(th, ret); if (res == 0) diff --git a/libsanitizer/lsan/lsan_preinit.cc b/libsanitizer/lsan/lsan_preinit.cc new file mode 100644 index 0000000..856f9f7 --- /dev/null +++ b/libsanitizer/lsan/lsan_preinit.cc @@ -0,0 +1,24 @@ +//===-- lsan_preinit.cc ---------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of LeakSanitizer. +// +// Call __lsan_init at the very early stage of process startup. +//===----------------------------------------------------------------------===// + +#include "lsan.h" + +#ifndef LSAN_USE_PREINIT_ARRAY +#define LSAN_USE_PREINIT_ARRAY 1 +#endif + +#if LSAN_USE_PREINIT_ARRAY && !defined(PIC) + // We force __lsan_init to be called before anyone else by placing it into + // .preinit_array section. + __attribute__((section(".preinit_array"), used)) + void (*__local_lsan_preinit)(void) = __lsan_init; +#endif diff --git a/libsanitizer/lsan/lsan_thread.cc b/libsanitizer/lsan/lsan_thread.cc index c260972..07f9d0a 100644 --- a/libsanitizer/lsan/lsan_thread.cc +++ b/libsanitizer/lsan/lsan_thread.cc @@ -143,6 +143,10 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, return true; } +void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback, + void *arg) { +} + void LockThreadRegistry() { thread_registry->Lock(); } diff --git a/libsanitizer/sanitizer_common/Makefile.am b/libsanitizer/sanitizer_common/Makefile.am index 13071c6..979d52d 100644 --- a/libsanitizer/sanitizer_common/Makefile.am +++ b/libsanitizer/sanitizer_common/Makefile.am @@ -14,22 +14,28 @@ sanitizer_common_files = \ sanitizer_allocator.cc \ sanitizer_common.cc \ sanitizer_common_libcdep.cc \ + sanitizer_coverage.cc \ sanitizer_flags.cc \ sanitizer_libc.cc \ + sanitizer_libignore.cc \ sanitizer_linux.cc \ sanitizer_linux_libcdep.cc \ sanitizer_mac.cc \ sanitizer_platform_limits_linux.cc \ sanitizer_platform_limits_posix.cc \ - sanitizer_posix.cc \ sanitizer_posix_libcdep.cc \ + sanitizer_posix.cc \ sanitizer_printf.cc \ sanitizer_stackdepot.cc \ sanitizer_stacktrace.cc \ + sanitizer_stacktrace_libcdep.cc \ sanitizer_stoptheworld_linux_libcdep.cc \ sanitizer_suppressions.cc \ sanitizer_symbolizer_posix_libcdep.cc \ sanitizer_symbolizer_win.cc \ + sanitizer_symbolizer.cc \ + sanitizer_symbolizer_libbacktrace.cc \ + sanitizer_symbolizer_libcdep.cc \ sanitizer_thread_registry.cc \ sanitizer_win.cc diff --git a/libsanitizer/sanitizer_common/Makefile.in b/libsanitizer/sanitizer_common/Makefile.in index 9bbdfd6..032cca4 100644 --- a/libsanitizer/sanitizer_common/Makefile.in +++ b/libsanitizer/sanitizer_common/Makefile.in @@ -56,17 +56,19 @@ CONFIG_CLEAN_VPATH_FILES = LTLIBRARIES = $(noinst_LTLIBRARIES) libsanitizer_common_la_LIBADD = am__objects_1 = sanitizer_allocator.lo sanitizer_common.lo \ - sanitizer_common_libcdep.lo sanitizer_flags.lo \ - sanitizer_libc.lo sanitizer_linux.lo \ - sanitizer_linux_libcdep.lo sanitizer_mac.lo \ + sanitizer_common_libcdep.lo sanitizer_coverage.lo \ + sanitizer_flags.lo sanitizer_libc.lo sanitizer_libignore.lo \ + sanitizer_linux.lo sanitizer_linux_libcdep.lo sanitizer_mac.lo \ sanitizer_platform_limits_linux.lo \ - sanitizer_platform_limits_posix.lo sanitizer_posix.lo \ - sanitizer_posix_libcdep.lo sanitizer_printf.lo \ - sanitizer_stackdepot.lo sanitizer_stacktrace.lo \ + sanitizer_platform_limits_posix.lo sanitizer_posix_libcdep.lo \ + sanitizer_posix.lo sanitizer_printf.lo sanitizer_stackdepot.lo \ + sanitizer_stacktrace.lo sanitizer_stacktrace_libcdep.lo \ sanitizer_stoptheworld_linux_libcdep.lo \ sanitizer_suppressions.lo \ sanitizer_symbolizer_posix_libcdep.lo \ - sanitizer_symbolizer_win.lo sanitizer_thread_registry.lo \ + sanitizer_symbolizer_win.lo sanitizer_symbolizer.lo \ + sanitizer_symbolizer_libbacktrace.lo \ + sanitizer_symbolizer_libcdep.lo sanitizer_thread_registry.lo \ sanitizer_win.lo am_libsanitizer_common_la_OBJECTS = $(am__objects_1) libsanitizer_common_la_OBJECTS = $(am_libsanitizer_common_la_OBJECTS) @@ -235,22 +237,28 @@ sanitizer_common_files = \ sanitizer_allocator.cc \ sanitizer_common.cc \ sanitizer_common_libcdep.cc \ + sanitizer_coverage.cc \ sanitizer_flags.cc \ sanitizer_libc.cc \ + sanitizer_libignore.cc \ sanitizer_linux.cc \ sanitizer_linux_libcdep.cc \ sanitizer_mac.cc \ sanitizer_platform_limits_linux.cc \ sanitizer_platform_limits_posix.cc \ - sanitizer_posix.cc \ sanitizer_posix_libcdep.cc \ + sanitizer_posix.cc \ sanitizer_printf.cc \ sanitizer_stackdepot.cc \ sanitizer_stacktrace.cc \ + sanitizer_stacktrace_libcdep.cc \ sanitizer_stoptheworld_linux_libcdep.cc \ sanitizer_suppressions.cc \ sanitizer_symbolizer_posix_libcdep.cc \ sanitizer_symbolizer_win.cc \ + sanitizer_symbolizer.cc \ + sanitizer_symbolizer_libbacktrace.cc \ + sanitizer_symbolizer_libcdep.cc \ sanitizer_thread_registry.cc \ sanitizer_win.cc @@ -350,8 +358,10 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_allocator.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_common_libcdep.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_coverage.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_flags.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_libignore.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_linux_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_mac.Plo@am__quote@ @@ -362,8 +372,12 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_printf.Plo@am__quote@ @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_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@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libbacktrace.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_posix_libcdep.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_symbolizer_win.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sanitizer_thread_registry.Plo@am__quote@ diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator.h b/libsanitizer/sanitizer_common/sanitizer_allocator.h index 505fa5b..8ba825f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator.h @@ -585,7 +585,69 @@ class FlatByteMap { u8 map_[kSize]; }; -// FIXME: Also implement TwoLevelByteMap. +// TwoLevelByteMap maps integers in range [0, kSize1*kSize2) to u8 values. +// It is implemented as a two-dimensional array: array of kSize1 pointers +// to kSize2-byte arrays. The secondary arrays are mmaped on demand. +// Each value is initially zero and can be set to something else only once. +// Setting and getting values from multiple threads is safe w/o extra locking. +template +class TwoLevelByteMap { + public: + void TestOnlyInit() { + internal_memset(map1_, 0, sizeof(map1_)); + mu_.Init(); + } + void TestOnlyUnmap() { + for (uptr i = 0; i < kSize1; i++) { + u8 *p = Get(i); + if (!p) continue; + MapUnmapCallback().OnUnmap(reinterpret_cast(p), kSize2); + UnmapOrDie(p, kSize2); + } + } + + uptr size() const { return kSize1 * kSize2; } + uptr size1() const { return kSize1; } + uptr size2() const { return kSize2; } + + void set(uptr idx, u8 val) { + CHECK_LT(idx, kSize1 * kSize2); + u8 *map2 = GetOrCreate(idx / kSize2); + CHECK_EQ(0U, map2[idx % kSize2]); + map2[idx % kSize2] = val; + } + + u8 operator[] (uptr idx) const { + CHECK_LT(idx, kSize1 * kSize2); + u8 *map2 = Get(idx / kSize2); + if (!map2) return 0; + return map2[idx % kSize2]; + } + + private: + u8 *Get(uptr idx) const { + CHECK_LT(idx, kSize1); + return reinterpret_cast( + atomic_load(&map1_[idx], memory_order_acquire)); + } + + u8 *GetOrCreate(uptr idx) { + u8 *res = Get(idx); + if (!res) { + SpinMutexLock l(&mu_); + if (!(res = Get(idx))) { + res = (u8*)MmapOrDie(kSize2, "TwoLevelByteMap"); + MapUnmapCallback().OnMap(reinterpret_cast(res), kSize2); + atomic_store(&map1_[idx], reinterpret_cast(res), + memory_order_release); + } + } + return res; + } + + atomic_uintptr_t map1_[kSize1]; + StaticSpinMutex mu_; +}; // SizeClassAllocator32 -- allocator for 32-bit address space. // This allocator can theoretically be used on 64-bit arch, but there it is less @@ -1049,6 +1111,7 @@ class LargeMmapAllocator { // This function does the same as GetBlockBegin, but is much faster. // Must be called with the allocator locked. void *GetBlockBeginFastLocked(void *ptr) { + mutex_.CheckLocked(); uptr p = reinterpret_cast(ptr); uptr n = n_chunks_; if (!n) return 0; @@ -1181,14 +1244,15 @@ class CombinedAllocator { if (alignment > 8) size = RoundUpTo(size, alignment); void *res; - if (primary_.CanAllocate(size, alignment)) + bool from_primary = primary_.CanAllocate(size, alignment); + if (from_primary) res = cache->Allocate(&primary_, primary_.ClassID(size)); else res = secondary_.Allocate(&stats_, size, alignment); if (alignment > 8) CHECK_EQ(reinterpret_cast(res) & (alignment - 1), 0); - if (cleared && res) - internal_memset(res, 0, size); + if (cleared && res && from_primary) + internal_bzero_aligned16(res, RoundUpTo(size, 16)); return res; } diff --git a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h index c033b96..efdb89e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h +++ b/libsanitizer/sanitizer_common/sanitizer_allocator_internal.h @@ -25,21 +25,25 @@ static const uptr kInternalAllocatorSpace = 0; #if SANITIZER_WORDSIZE == 32 static const u64 kInternalAllocatorSize = (1ULL << 32); static const uptr kInternalAllocatorRegionSizeLog = 20; +static const uptr kInternalAllocatorNumRegions = + kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; +typedef FlatByteMap ByteMap; #else static const u64 kInternalAllocatorSize = (1ULL << 47); static const uptr kInternalAllocatorRegionSizeLog = 24; -#endif -static const uptr kInternalAllocatorFlatByteMapSize = +static const uptr kInternalAllocatorNumRegions = kInternalAllocatorSize >> kInternalAllocatorRegionSizeLog; +typedef TwoLevelByteMap<(kInternalAllocatorNumRegions >> 12), 1 << 12> ByteMap; +#endif typedef SizeClassAllocator32< kInternalAllocatorSpace, kInternalAllocatorSize, 16, InternalSizeClassMap, - kInternalAllocatorRegionSizeLog, - FlatByteMap > PrimaryInternalAllocator; + kInternalAllocatorRegionSizeLog, ByteMap> PrimaryInternalAllocator; typedef SizeClassAllocatorLocalCache InternalAllocatorCache; -// We don't want our internal allocator to do any map/unmap operations. +// We don't want our internal allocator to do any map/unmap operations from +// LargeMmapAllocator. struct CrashOnMapUnmap { void OnMap(uptr p, uptr size) const { RAW_CHECK_MSG(0, "Unexpected mmap in InternalAllocator!"); diff --git a/libsanitizer/sanitizer_common/sanitizer_common.cc b/libsanitizer/sanitizer_common/sanitizer_common.cc index f689df4..bf73dc6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.cc +++ b/libsanitizer/sanitizer_common/sanitizer_common.cc @@ -10,12 +10,14 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_libc.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_symbolizer.h" namespace __sanitizer { const char *SanitizerToolName = "SanitizerTool"; -uptr SanitizerVerbosity = 0; uptr GetPageSizeCached() { static uptr PageSize; @@ -134,14 +136,71 @@ void *MmapAlignedOrDie(uptr size, uptr alignment, const char *mem_type) { return (void*)res; } +const char *StripPathPrefix(const char *filepath, + const char *strip_path_prefix) { + if (filepath == 0) return 0; + if (strip_path_prefix == 0) return filepath; + const char *pos = internal_strstr(filepath, strip_path_prefix); + if (pos == 0) return filepath; + pos += internal_strlen(strip_path_prefix); + if (pos[0] == '.' && pos[1] == '/') + pos += 2; + 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); +} + +void ReportErrorSummary(const char *error_message) { + if (!common_flags()->print_summary) + return; + InternalScopedBuffer buff(kMaxSummaryLength); + internal_snprintf(buff.data(), buff.size(), + "SUMMARY: %s: %s", SanitizerToolName, error_message); + __sanitizer_report_error_summary(buff.data()); +} + void ReportErrorSummary(const char *error_type, const char *file, int line, const char *function) { - const int kMaxSize = 1024; // We don't want a summary too long. - InternalScopedBuffer buff(kMaxSize); - internal_snprintf(buff.data(), kMaxSize, "%s: %s %s:%d %s", - SanitizerToolName, error_type, - file ? file : "??", line, function ? function : "??"); - __sanitizer_report_error_summary(buff.data()); + if (!common_flags()->print_summary) + return; + InternalScopedBuffer buff(kMaxSummaryLength); + internal_snprintf( + buff.data(), buff.size(), "%s %s:%d %s", error_type, + file ? StripPathPrefix(file, common_flags()->strip_path_prefix) : "??", + line, function ? function : "??"); + ReportErrorSummary(buff.data()); +} + +void ReportErrorSummary(const char *error_type, StackTrace *stack) { + if (!common_flags()->print_summary) + return; + AddressInfo ai; +#if !SANITIZER_GO + if (stack->size > 0 && Symbolizer::Get()->IsAvailable()) { + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + Symbolizer::Get()->SymbolizeCode(pc, &ai, 1); + } +#endif + ReportErrorSummary(error_type, ai.file, ai.line, ai.function); } LoadedModule::LoadedModule(const char *module_name, uptr base_address) { @@ -165,13 +224,25 @@ 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); +} + } // namespace __sanitizer using namespace __sanitizer; // NOLINT extern "C" { void __sanitizer_set_report_path(const char *path) { - if (!path) return; + if (!path) + return; uptr len = internal_strlen(path); if (len > sizeof(report_path_prefix) - 100) { Report("ERROR: Path is too long: %c%c%c%c%c%c%c%c...\n", @@ -179,18 +250,21 @@ void __sanitizer_set_report_path(const char *path) { path[4], path[5], path[6], path[7]); Die(); } - internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix)); - report_path_prefix[len] = '\0'; - report_fd = kInvalidFd; - log_to_file = true; -} - -void __sanitizer_set_report_fd(int fd) { if (report_fd != kStdoutFd && report_fd != kStderrFd && report_fd != kInvalidFd) internal_close(report_fd); - report_fd = fd; + report_fd = kInvalidFd; + log_to_file = false; + if (internal_strcmp(path, "stdout") == 0) { + report_fd = kStdoutFd; + } else if (internal_strcmp(path, "stderr") == 0) { + report_fd = kStderrFd; + } else { + internal_strncpy(report_path_prefix, path, sizeof(report_path_prefix)); + report_path_prefix[len] = '\0'; + log_to_file = true; + } } void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { @@ -199,6 +273,6 @@ void NOINLINE __sanitizer_sandbox_on_notify(void *reserved) { } void __sanitizer_report_error_summary(const char *error_summary) { - Printf("SUMMARY: %s\n", error_summary); + Printf("%s\n", error_summary); } } // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_common.h b/libsanitizer/sanitizer_common/sanitizer_common.h index 417f71f..833c2b0 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common.h +++ b/libsanitizer/sanitizer_common/sanitizer_common.h @@ -34,7 +34,6 @@ const uptr kCacheLineSize = 64; const uptr kMaxPathLength = 512; extern const char *SanitizerToolName; // Can be changed by the tool. -extern uptr SanitizerVerbosity; uptr GetPageSize(); uptr GetPageSizeCached(); @@ -86,6 +85,23 @@ class InternalScopedBuffer { void operator=(const InternalScopedBuffer&); }; +class InternalScopedString : public InternalScopedBuffer { + public: + explicit InternalScopedString(uptr max_length) + : InternalScopedBuffer(max_length), length_(0) { + (*this)[0] = '\0'; + } + uptr length() { return length_; } + void clear() { + (*this)[0] = '\0'; + length_ = 0; + } + void append(const char *format, ...); + + private: + uptr length_; +}; + // Simple low-level (mmap-based) allocator for internal use. Doesn't have // constructor, so all instances of LowLevelAllocator should be // linker initialized. @@ -110,6 +126,7 @@ bool PrintsToTtyCached(); void Printf(const char *format, ...); void Report(const char *format, ...); void SetPrintfAndReportCallback(void (*callback)(const char *)); + // Can be used to prevent mixing error reports from different sanitizers. extern StaticSpinMutex CommonSanitizerReportMutex; void MaybeOpenReportFile(); @@ -130,6 +147,14 @@ uptr ReadFileToBuffer(const char *file_name, char **buff, // in '*buff_size'. void *MapFileToMemory(const char *file_name, uptr *buff_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); + // OS void DisableCoreDumper(); void DumpProcessMap(); @@ -153,6 +178,9 @@ 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(); @@ -176,11 +204,17 @@ typedef void (*CheckFailedCallbackType)(const char *, int, const char *, u64, u64); void SetCheckFailedCallback(CheckFailedCallbackType callback); -// Construct a one-line string like -// SanitizerToolName: error_type file:line function -// and call __sanitizer_report_error_summary on it. +// We don't want a summary too long. +const int kMaxSummaryLength = 1024; +// Construct a one-line string: +// SUMMARY: SanitizerToolName: error_message +// and pass it to __sanitizer_report_error_summary. +void ReportErrorSummary(const char *error_message); +// Same as above, but construct error_message as: +// error_type: file:line function void ReportErrorSummary(const char *error_type, const char *file, int line, const char *function); +void ReportErrorSummary(const char *error_type, StackTrace *trace); // Math #if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__) @@ -326,6 +360,8 @@ class InternalMmapVector { return capacity_; } + void clear() { size_ = 0; } + private: void Resize(uptr new_capacity) { CHECK_GT(new_capacity, 0); @@ -431,6 +467,20 @@ typedef bool (*string_predicate_t)(const char *); uptr GetListOfModules(LoadedModule *modules, uptr max_modules, string_predicate_t filter); +#if SANITIZER_POSIX +const uptr kPthreadDestructorIterations = 4; +#else +// Unused on Windows. +const uptr kPthreadDestructorIterations = 0; +#endif + +// Callback type for iterating over a set of memory ranges. +typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg); } // namespace __sanitizer +inline void *operator new(__sanitizer::operator_new_size_type size, + __sanitizer::LowLevelAllocator &alloc) { + return alloc.Allocate(size); +} + #endif // SANITIZER_COMMON_H diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc index 17ef72e..45b12fc 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors.inc @@ -13,19 +13,51 @@ // COMMON_INTERCEPTOR_ENTER // COMMON_INTERCEPTOR_READ_RANGE // COMMON_INTERCEPTOR_WRITE_RANGE +// COMMON_INTERCEPTOR_INITIALIZE_RANGE // COMMON_INTERCEPTOR_FD_ACQUIRE // COMMON_INTERCEPTOR_FD_RELEASE +// COMMON_INTERCEPTOR_FD_ACCESS // COMMON_INTERCEPTOR_SET_THREAD_NAME +// COMMON_INTERCEPTOR_ON_EXIT +// COMMON_INTERCEPTOR_MUTEX_LOCK +// COMMON_INTERCEPTOR_MUTEX_UNLOCK +// COMMON_INTERCEPTOR_MUTEX_REPAIR +// COMMON_INTERCEPTOR_SET_PTHREAD_NAME +// COMMON_INTERCEPTOR_HANDLE_RECVMSG //===----------------------------------------------------------------------===// #include "interception/interception.h" #include "sanitizer_platform_interceptors.h" #include -#if SANITIZER_WINDOWS +#if SANITIZER_WINDOWS && !defined(va_copy) #define va_copy(dst, src) ((dst) = (src)) #endif // _WIN32 +#ifndef COMMON_INTERCEPTOR_INITIALIZE_RANGE +#define COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, p, size) {} +#endif + +#ifndef COMMON_INTERCEPTOR_FD_ACCESS +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_LOCK +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_UNLOCK +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_MUTEX_REPAIR +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) {} +#endif + +#ifndef COMMON_INTERCEPTOR_HANDLE_RECVMSG +#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) ((void)(msg)) +#endif + #if SANITIZER_INTERCEPT_STRCMP static inline int CharCmpX(unsigned char c1, unsigned char c2) { return (c1 == c2) ? 0 : (c1 < c2) ? -1 : 1; @@ -36,7 +68,7 @@ INTERCEPTOR(int, strcmp, const char *s1, const char *s2) { COMMON_INTERCEPTOR_ENTER(ctx, strcmp, s1, s2); unsigned char c1, c2; uptr i; - for (i = 0; ; i++) { + for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; if (c1 != c2 || c1 == '\0') break; @@ -61,8 +93,8 @@ INTERCEPTOR(int, strncmp, const char *s1, const char *s2, uptr size) { return CharCmpX(c1, c2); } -#define INIT_STRCMP INTERCEPT_FUNCTION(strcmp) -#define INIT_STRNCMP INTERCEPT_FUNCTION(strncmp) +#define INIT_STRCMP COMMON_INTERCEPT_FUNCTION(strcmp) +#define INIT_STRNCMP COMMON_INTERCEPT_FUNCTION(strncmp) #else #define INIT_STRCMP #define INIT_STRNCMP @@ -80,11 +112,10 @@ INTERCEPTOR(int, strcasecmp, const char *s1, const char *s2) { COMMON_INTERCEPTOR_ENTER(ctx, strcasecmp, s1, s2); unsigned char c1 = 0, c2 = 0; uptr i; - for (i = 0; ; i++) { + for (i = 0;; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') - break; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, i + 1); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, i + 1); @@ -99,16 +130,15 @@ INTERCEPTOR(int, strncasecmp, const char *s1, const char *s2, SIZE_T n) { for (i = 0; i < n; i++) { c1 = (unsigned char)s1[i]; c2 = (unsigned char)s2[i]; - if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') - break; + if (CharCaseCmp(c1, c2) != 0 || c1 == '\0') break; } COMMON_INTERCEPTOR_READ_RANGE(ctx, s1, Min(i + 1, n)); COMMON_INTERCEPTOR_READ_RANGE(ctx, s2, Min(i + 1, n)); return CharCaseCmp(c1, c2); } -#define INIT_STRCASECMP INTERCEPT_FUNCTION(strcasecmp) -#define INIT_STRNCASECMP INTERCEPT_FUNCTION(strncasecmp) +#define INIT_STRCASECMP COMMON_INTERCEPT_FUNCTION(strcasecmp) +#define INIT_STRNCASECMP COMMON_INTERCEPT_FUNCTION(strncasecmp) #else #define INIT_STRCASECMP #define INIT_STRNCASECMP @@ -123,10 +153,10 @@ INTERCEPTOR(double, frexp, double x, int *exp) { return res; } -#define INIT_FREXP INTERCEPT_FUNCTION(frexp); +#define INIT_FREXP COMMON_INTERCEPT_FUNCTION(frexp); #else #define INIT_FREXP -#endif // SANITIZER_INTERCEPT_FREXP +#endif // SANITIZER_INTERCEPT_FREXP #if SANITIZER_INTERCEPT_FREXPF_FREXPL INTERCEPTOR(float, frexpf, float x, int *exp) { @@ -145,12 +175,12 @@ INTERCEPTOR(long double, frexpl, long double x, int *exp) { return res; } -#define INIT_FREXPF_FREXPL \ - INTERCEPT_FUNCTION(frexpf); \ - INTERCEPT_FUNCTION(frexpl) +#define INIT_FREXPF_FREXPL \ + COMMON_INTERCEPT_FUNCTION(frexpf); \ + COMMON_INTERCEPT_FUNCTION(frexpl) #else #define INIT_FREXPF_FREXPL -#endif // SANITIZER_INTERCEPT_FREXPF_FREXPL +#endif // SANITIZER_INTERCEPT_FREXPF_FREXPL #if SI_NOT_WINDOWS static void write_iovec(void *ctx, struct __sanitizer_iovec *iovec, @@ -177,14 +207,13 @@ static void read_iovec(void *ctx, struct __sanitizer_iovec *iovec, INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, read, fd, ptr, count); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(read)(fd, ptr, count); - if (res > 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); - if (res >= 0 && fd >= 0) - COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_READ INTERCEPT_FUNCTION(read) +#define INIT_READ COMMON_INTERCEPT_FUNCTION(read) #else #define INIT_READ #endif @@ -193,14 +222,13 @@ INTERCEPTOR(SSIZE_T, read, int fd, void *ptr, SIZE_T count) { INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(pread)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); - if (res >= 0 && fd >= 0) - COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_PREAD INTERCEPT_FUNCTION(pread) +#define INIT_PREAD COMMON_INTERCEPT_FUNCTION(pread) #else #define INIT_PREAD #endif @@ -209,14 +237,13 @@ INTERCEPTOR(SSIZE_T, pread, int fd, void *ptr, SIZE_T count, OFF_T offset) { INTERCEPTOR(SSIZE_T, pread64, int fd, void *ptr, SIZE_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pread64, fd, ptr, count, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(pread64)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); - if (res >= 0 && fd >= 0) - COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); + if (res > 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, res); + if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_PREAD64 INTERCEPT_FUNCTION(pread64) +#define INIT_PREAD64 COMMON_INTERCEPT_FUNCTION(pread64) #else #define INIT_PREAD64 #endif @@ -226,12 +253,13 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, readv, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readv, fd, iov, iovcnt); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(readv)(fd, iov, iovcnt); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_READV INTERCEPT_FUNCTION(readv) +#define INIT_READV COMMON_INTERCEPT_FUNCTION(readv) #else #define INIT_READV #endif @@ -241,12 +269,13 @@ INTERCEPTOR(SSIZE_T, preadv, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_PREADV INTERCEPT_FUNCTION(preadv) +#define INIT_PREADV COMMON_INTERCEPT_FUNCTION(preadv) #else #define INIT_PREADV #endif @@ -256,12 +285,13 @@ INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, preadv64, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); SSIZE_T res = REAL(preadv64)(fd, iov, iovcnt, offset); if (res > 0) write_iovec(ctx, iov, iovcnt, res); if (res >= 0 && fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); return res; } -#define INIT_PREADV64 INTERCEPT_FUNCTION(preadv64) +#define INIT_PREADV64 COMMON_INTERCEPT_FUNCTION(preadv64) #else #define INIT_PREADV64 #endif @@ -270,15 +300,14 @@ INTERCEPTOR(SSIZE_T, preadv64, int fd, __sanitizer_iovec *iov, int iovcnt, INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, write, fd, ptr, count); - if (fd >= 0) - COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(write)(fd, ptr, count); // FIXME: this check should be _before_ the call to REAL(write), not after - if (res > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -#define INIT_WRITE INTERCEPT_FUNCTION(write) +#define INIT_WRITE COMMON_INTERCEPT_FUNCTION(write) #else #define INIT_WRITE #endif @@ -287,14 +316,13 @@ INTERCEPTOR(SSIZE_T, write, int fd, void *ptr, SIZE_T count) { INTERCEPTOR(SSIZE_T, pwrite, int fd, void *ptr, SIZE_T count, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite, fd, ptr, count, offset); - if (fd >= 0) - COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -#define INIT_PWRITE INTERCEPT_FUNCTION(pwrite) +#define INIT_PWRITE COMMON_INTERCEPT_FUNCTION(pwrite) #else #define INIT_PWRITE #endif @@ -304,14 +332,13 @@ INTERCEPTOR(SSIZE_T, pwrite64, int fd, void *ptr, OFF64_T count, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwrite64, fd, ptr, count, offset); - if (fd >= 0) - COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); + if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwrite64)(fd, ptr, count, offset); - if (res > 0) - COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); + if (res > 0) COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, res); return res; } -#define INIT_PWRITE64 INTERCEPT_FUNCTION(pwrite64) +#define INIT_PWRITE64 COMMON_INTERCEPT_FUNCTION(pwrite64) #else #define INIT_PWRITE64 #endif @@ -321,12 +348,13 @@ INTERCEPTOR_WITH_SUFFIX(SSIZE_T, writev, int fd, __sanitizer_iovec *iov, int iovcnt) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, writev, fd, iov, iovcnt); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(writev)(fd, iov, iovcnt); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } -#define INIT_WRITEV INTERCEPT_FUNCTION(writev) +#define INIT_WRITEV COMMON_INTERCEPT_FUNCTION(writev) #else #define INIT_WRITEV #endif @@ -336,12 +364,13 @@ INTERCEPTOR(SSIZE_T, pwritev, int fd, __sanitizer_iovec *iov, int iovcnt, OFF_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } -#define INIT_PWRITEV INTERCEPT_FUNCTION(pwritev) +#define INIT_PWRITEV COMMON_INTERCEPT_FUNCTION(pwritev) #else #define INIT_PWRITEV #endif @@ -351,20 +380,21 @@ INTERCEPTOR(SSIZE_T, pwritev64, int fd, __sanitizer_iovec *iov, int iovcnt, OFF64_T offset) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, pwritev64, fd, iov, iovcnt, offset); + COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd); if (fd >= 0) COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd); SSIZE_T res = REAL(pwritev64)(fd, iov, iovcnt, offset); if (res > 0) read_iovec(ctx, iov, iovcnt, res); return res; } -#define INIT_PWRITEV64 INTERCEPT_FUNCTION(pwritev64) +#define INIT_PWRITEV64 COMMON_INTERCEPT_FUNCTION(pwritev64) #else #define INIT_PWRITEV64 #endif #if SANITIZER_INTERCEPT_PRCTL -INTERCEPTOR(int, prctl, int option, - unsigned long arg2, unsigned long arg3, // NOLINT - unsigned long arg4, unsigned long arg5) { // NOLINT +INTERCEPTOR(int, prctl, int option, unsigned long arg2, + unsigned long arg3, // NOLINT + unsigned long arg4, unsigned long arg5) { // NOLINT void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, prctl, option, arg2, arg3, arg4, arg5); static const int PR_SET_NAME = 15; @@ -377,11 +407,10 @@ INTERCEPTOR(int, prctl, int option, } return res; } -#define INIT_PRCTL INTERCEPT_FUNCTION(prctl) +#define INIT_PRCTL COMMON_INTERCEPT_FUNCTION(prctl) #else #define INIT_PRCTL -#endif // SANITIZER_INTERCEPT_PRCTL - +#endif // SANITIZER_INTERCEPT_PRCTL #if SANITIZER_INTERCEPT_TIME INTERCEPTOR(unsigned long, time, unsigned long *t) { @@ -393,51 +422,58 @@ INTERCEPTOR(unsigned long, time, unsigned long *t) { } return res; } -#define INIT_TIME \ - INTERCEPT_FUNCTION(time); +#define INIT_TIME COMMON_INTERCEPT_FUNCTION(time); #else #define INIT_TIME -#endif // SANITIZER_INTERCEPT_TIME - +#endif // SANITIZER_INTERCEPT_TIME #if SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS -INTERCEPTOR(void *, localtime, unsigned long *timep) { +static void unpoison_tm(void *ctx, __sanitizer_tm *tm) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + if (tm->tm_zone) { + // Can not use COMMON_INTERCEPTOR_WRITE_RANGE here, because tm->tm_zone + // can point to shared memory and tsan would report a data race. + COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, tm->tm_zone, + REAL(strlen(tm->tm_zone)) + 1); + } +} +INTERCEPTOR(__sanitizer_tm *, localtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime, timep); - void *res = REAL(localtime)(timep); + __sanitizer_tm *res = REAL(localtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } -INTERCEPTOR(void *, localtime_r, unsigned long *timep, void *result) { +INTERCEPTOR(__sanitizer_tm *, localtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, localtime_r, timep, result); - void *res = REAL(localtime_r)(timep, result); + __sanitizer_tm *res = REAL(localtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } -INTERCEPTOR(void *, gmtime, unsigned long *timep) { +INTERCEPTOR(__sanitizer_tm *, gmtime, unsigned long *timep) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime, timep); - void *res = REAL(gmtime)(timep); + __sanitizer_tm *res = REAL(gmtime)(timep); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } -INTERCEPTOR(void *, gmtime_r, unsigned long *timep, void *result) { +INTERCEPTOR(__sanitizer_tm *, gmtime_r, unsigned long *timep, void *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, gmtime_r, timep, result); - void *res = REAL(gmtime_r)(timep, result); + __sanitizer_tm *res = REAL(gmtime_r)(timep, result); if (res) { COMMON_INTERCEPTOR_READ_RANGE(ctx, timep, sizeof(*timep)); - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_tm_sz); + unpoison_tm(ctx, res); } return res; } @@ -461,38 +497,59 @@ INTERCEPTOR(char *, ctime_r, unsigned long *timep, char *result) { } return res; } -INTERCEPTOR(char *, asctime, void *tm) { +INTERCEPTOR(char *, asctime, __sanitizer_tm *tm) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime, tm); char *res = REAL(asctime)(tm); if (res) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, struct_tm_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } -INTERCEPTOR(char *, asctime_r, void *tm, char *result) { +INTERCEPTOR(char *, asctime_r, __sanitizer_tm *tm, char *result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, asctime_r, tm, result); char *res = REAL(asctime_r)(tm, result); if (res) { - COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, struct_tm_sz); + COMMON_INTERCEPTOR_READ_RANGE(ctx, tm, sizeof(*tm)); COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); } return res; } -#define INIT_LOCALTIME_AND_FRIENDS \ - INTERCEPT_FUNCTION(localtime); \ - INTERCEPT_FUNCTION(localtime_r); \ - INTERCEPT_FUNCTION(gmtime); \ - INTERCEPT_FUNCTION(gmtime_r); \ - INTERCEPT_FUNCTION(ctime); \ - INTERCEPT_FUNCTION(ctime_r); \ - INTERCEPT_FUNCTION(asctime); \ - INTERCEPT_FUNCTION(asctime_r); +#define INIT_LOCALTIME_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(localtime); \ + COMMON_INTERCEPT_FUNCTION(localtime_r); \ + COMMON_INTERCEPT_FUNCTION(gmtime); \ + COMMON_INTERCEPT_FUNCTION(gmtime_r); \ + COMMON_INTERCEPT_FUNCTION(ctime); \ + COMMON_INTERCEPT_FUNCTION(ctime_r); \ + COMMON_INTERCEPT_FUNCTION(asctime); \ + COMMON_INTERCEPT_FUNCTION(asctime_r); #else #define INIT_LOCALTIME_AND_FRIENDS -#endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS +#endif // SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS + +#if SANITIZER_INTERCEPT_STRPTIME +INTERCEPTOR(char *, strptime, char *s, char *format, __sanitizer_tm *tm) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, strptime, s, format, tm); + if (format) + COMMON_INTERCEPTOR_READ_RANGE(ctx, format, REAL(strlen)(format) + 1); + char *res = REAL(strptime)(s, format, tm); + if (res) { + COMMON_INTERCEPTOR_READ_RANGE(ctx, s, res - s); + // Do not call unpoison_tm here, because strptime does not, in fact, + // initialize the entire struct tm. For example, tm_zone pointer is left + // uninitialized. + if (tm) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tm, sizeof(*tm)); + } + return res; +} +#define INIT_STRPTIME COMMON_INTERCEPT_FUNCTION(strptime); +#else +#define INIT_STRPTIME +#endif #if SANITIZER_INTERCEPT_SCANF @@ -566,25 +623,25 @@ SCANF_INTERCEPTOR_IMPL(__isoc99_sscanf, __isoc99_vsscanf, str, format) #endif #if SANITIZER_INTERCEPT_SCANF -#define INIT_SCANF \ - INTERCEPT_FUNCTION(scanf); \ - INTERCEPT_FUNCTION(sscanf); \ - INTERCEPT_FUNCTION(fscanf); \ - INTERCEPT_FUNCTION(vscanf); \ - INTERCEPT_FUNCTION(vsscanf); \ - INTERCEPT_FUNCTION(vfscanf); +#define INIT_SCANF \ + COMMON_INTERCEPT_FUNCTION(scanf); \ + COMMON_INTERCEPT_FUNCTION(sscanf); \ + COMMON_INTERCEPT_FUNCTION(fscanf); \ + COMMON_INTERCEPT_FUNCTION(vscanf); \ + COMMON_INTERCEPT_FUNCTION(vsscanf); \ + COMMON_INTERCEPT_FUNCTION(vfscanf); #else #define INIT_SCANF #endif #if SANITIZER_INTERCEPT_ISOC99_SCANF -#define INIT_ISOC99_SCANF \ - INTERCEPT_FUNCTION(__isoc99_scanf); \ - INTERCEPT_FUNCTION(__isoc99_sscanf); \ - INTERCEPT_FUNCTION(__isoc99_fscanf); \ - INTERCEPT_FUNCTION(__isoc99_vscanf); \ - INTERCEPT_FUNCTION(__isoc99_vsscanf); \ - INTERCEPT_FUNCTION(__isoc99_vfscanf); +#define INIT_ISOC99_SCANF \ + COMMON_INTERCEPT_FUNCTION(__isoc99_scanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_sscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_fscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vsscanf); \ + COMMON_INTERCEPT_FUNCTION(__isoc99_vfscanf); #else #define INIT_ISOC99_SCANF #endif @@ -599,45 +656,38 @@ INTERCEPTOR(int, ioctl, int d, unsigned request, void *arg) { // Note: TSan does not use common flags, and they are zero-initialized. // This effectively disables ioctl handling in TSan. - if (!common_flags()->handle_ioctl) - return REAL(ioctl)(d, request, arg); + if (!common_flags()->handle_ioctl) return REAL(ioctl)(d, request, arg); const ioctl_desc *desc = ioctl_lookup(request); - if (!desc) - Printf("WARNING: unknown ioctl %x\n", request); + if (!desc) Printf("WARNING: unknown ioctl %x\n", request); - if (desc) - ioctl_common_pre(ctx, desc, d, request, arg); + if (desc) ioctl_common_pre(ctx, desc, d, request, arg); int res = REAL(ioctl)(d, request, arg); // FIXME: some ioctls have different return values for success and failure. - if (desc && res != -1) - ioctl_common_post(ctx, desc, res, d, request, arg); + if (desc && res != -1) ioctl_common_post(ctx, desc, res, d, request, arg); return res; } #define INIT_IOCTL \ ioctl_init(); \ - INTERCEPT_FUNCTION(ioctl); + COMMON_INTERCEPT_FUNCTION(ioctl); #else #define INIT_IOCTL #endif - #if SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS INTERCEPTOR(void *, getpwnam, const char *name) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); void *res = REAL(getpwnam)(name); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); return res; } INTERCEPTOR(void *, getpwuid, u32 uid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid, uid); void *res = REAL(getpwuid)(uid); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_passwd_sz); return res; } INTERCEPTOR(void *, getgrnam, const char *name) { @@ -645,31 +695,28 @@ INTERCEPTOR(void *, getgrnam, const char *name) { COMMON_INTERCEPTOR_ENTER(ctx, getgrnam, name); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); void *res = REAL(getgrnam)(name); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); return res; } INTERCEPTOR(void *, getgrgid, u32 gid) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid, gid); void *res = REAL(getgrgid)(gid); - if (res != 0) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); + if (res != 0) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, struct_group_sz); return res; } -#define INIT_GETPWNAM_AND_FRIENDS \ - INTERCEPT_FUNCTION(getpwnam); \ - INTERCEPT_FUNCTION(getpwuid); \ - INTERCEPT_FUNCTION(getgrnam); \ - INTERCEPT_FUNCTION(getgrgid); +#define INIT_GETPWNAM_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(getpwnam); \ + COMMON_INTERCEPT_FUNCTION(getpwuid); \ + COMMON_INTERCEPT_FUNCTION(getgrnam); \ + COMMON_INTERCEPT_FUNCTION(getgrgid); #else #define INIT_GETPWNAM_AND_FRIENDS #endif - #if SANITIZER_INTERCEPT_GETPWNAM_R_AND_FRIENDS -INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, char *buf, + SIZE_T buflen, void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwnam_r, name, pwd, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); @@ -680,8 +727,8 @@ INTERCEPTOR(int, getpwnam_r, const char *name, void *pwd, } return res; } -INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, char *buf, SIZE_T buflen, + void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getpwuid_r, uid, pwd, buf, buflen, result); int res = REAL(getpwuid_r)(uid, pwd, buf, buflen, result); @@ -691,8 +738,8 @@ INTERCEPTOR(int, getpwuid_r, u32 uid, void *pwd, } return res; } -INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, char *buf, + SIZE_T buflen, void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrnam_r, name, grp, buf, buflen, result); COMMON_INTERCEPTOR_READ_RANGE(ctx, name, REAL(strlen)(name) + 1); @@ -703,8 +750,8 @@ INTERCEPTOR(int, getgrnam_r, const char *name, void *grp, } return res; } -INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, - char *buf, SIZE_T buflen, void **result) { +INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, char *buf, SIZE_T buflen, + void **result) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgrgid_r, gid, grp, buf, buflen, result); int res = REAL(getgrgid_r)(gid, grp, buf, buflen, result); @@ -714,16 +761,15 @@ INTERCEPTOR(int, getgrgid_r, u32 gid, void *grp, } return res; } -#define INIT_GETPWNAM_R_AND_FRIENDS \ - INTERCEPT_FUNCTION(getpwnam_r); \ - INTERCEPT_FUNCTION(getpwuid_r); \ - INTERCEPT_FUNCTION(getgrnam_r); \ - INTERCEPT_FUNCTION(getgrgid_r); +#define INIT_GETPWNAM_R_AND_FRIENDS \ + COMMON_INTERCEPT_FUNCTION(getpwnam_r); \ + COMMON_INTERCEPT_FUNCTION(getpwuid_r); \ + COMMON_INTERCEPT_FUNCTION(getgrnam_r); \ + COMMON_INTERCEPT_FUNCTION(getgrgid_r); #else #define INIT_GETPWNAM_R_AND_FRIENDS #endif - #if SANITIZER_INTERCEPT_CLOCK_GETTIME INTERCEPTOR(int, clock_getres, u32 clk_id, void *tp) { void *ctx; @@ -749,15 +795,14 @@ INTERCEPTOR(int, clock_settime, u32 clk_id, const void *tp) { COMMON_INTERCEPTOR_READ_RANGE(ctx, tp, struct_timespec_sz); return REAL(clock_settime)(clk_id, tp); } -#define INIT_CLOCK_GETTIME \ - INTERCEPT_FUNCTION(clock_getres); \ - INTERCEPT_FUNCTION(clock_gettime); \ - INTERCEPT_FUNCTION(clock_settime); +#define INIT_CLOCK_GETTIME \ + COMMON_INTERCEPT_FUNCTION(clock_getres); \ + COMMON_INTERCEPT_FUNCTION(clock_gettime); \ + COMMON_INTERCEPT_FUNCTION(clock_settime); #else #define INIT_CLOCK_GETTIME #endif - #if SANITIZER_INTERCEPT_GETITIMER INTERCEPTOR(int, getitimer, int which, void *curr_value) { void *ctx; @@ -779,9 +824,9 @@ INTERCEPTOR(int, setitimer, int which, const void *new_value, void *old_value) { } return res; } -#define INIT_GETITIMER \ - INTERCEPT_FUNCTION(getitimer); \ - INTERCEPT_FUNCTION(setitimer); +#define INIT_GETITIMER \ + COMMON_INTERCEPT_FUNCTION(getitimer); \ + COMMON_INTERCEPT_FUNCTION(setitimer); #else #define INIT_GETITIMER #endif @@ -799,8 +844,8 @@ static void unpoison_glob_t(void *ctx, __sanitizer_glob_t *pglob) { } } -static THREADLOCAL __sanitizer_glob_t* pglob_copy; -static THREADLOCAL void* glob_ctx; +static THREADLOCAL __sanitizer_glob_t *pglob_copy; +static THREADLOCAL void *glob_ctx; static void wrapped_gl_closedir(void *dir) { COMMON_INTERCEPTOR_UNPOISON_PARAM(glob_ctx, 1); @@ -835,9 +880,10 @@ INTERCEPTOR(int, glob, const char *pattern, int flags, __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob, pattern, flags, errfunc, pglob); - __sanitizer_glob_t glob_copy = {0, 0, 0, 0, wrapped_gl_closedir, - wrapped_gl_readdir, wrapped_gl_opendir, - wrapped_gl_lstat, wrapped_gl_stat}; + __sanitizer_glob_t glob_copy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); @@ -866,9 +912,10 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags, __sanitizer_glob_t *pglob) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, glob64, pattern, flags, errfunc, pglob); - __sanitizer_glob_t glob_copy = {0, 0, 0, 0, wrapped_gl_closedir, - wrapped_gl_readdir, wrapped_gl_opendir, - wrapped_gl_lstat, wrapped_gl_stat}; + __sanitizer_glob_t glob_copy = { + 0, 0, 0, + 0, wrapped_gl_closedir, wrapped_gl_readdir, + wrapped_gl_opendir, wrapped_gl_lstat, wrapped_gl_stat}; if (flags & glob_altdirfunc) { Swap(pglob->gl_closedir, glob_copy.gl_closedir); Swap(pglob->gl_readdir, glob_copy.gl_readdir); @@ -891,9 +938,9 @@ INTERCEPTOR(int, glob64, const char *pattern, int flags, if ((!res || res == glob_nomatch) && pglob) unpoison_glob_t(ctx, pglob); return res; } -#define INIT_GLOB \ - INTERCEPT_FUNCTION(glob); \ - INTERCEPT_FUNCTION(glob64); +#define INIT_GLOB \ + COMMON_INTERCEPT_FUNCTION(glob); \ + COMMON_INTERCEPT_FUNCTION(glob64); #else // SANITIZER_INTERCEPT_GLOB #define INIT_GLOB #endif // SANITIZER_INTERCEPT_GLOB @@ -911,7 +958,7 @@ INTERCEPTOR_WITH_SUFFIX(int, wait, int *status) { return res; } INTERCEPTOR_WITH_SUFFIX(int, waitid, int idtype, int id, void *infop, - int options) { + int options) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, waitid, idtype, id, infop, options); int res = REAL(waitid)(idtype, id, infop, options); @@ -932,10 +979,8 @@ INTERCEPTOR(int, wait3, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, wait3, status, options, rusage); int res = REAL(wait3)(status, options, rusage); if (res != -1) { - if (status) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); - if (rusage) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); + if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } @@ -944,19 +989,17 @@ INTERCEPTOR(int, wait4, int pid, int *status, int options, void *rusage) { COMMON_INTERCEPTOR_ENTER(ctx, wait4, pid, status, options, rusage); int res = REAL(wait4)(pid, status, options, rusage); if (res != -1) { - if (status) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); - if (rusage) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); + if (status) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, status, sizeof(*status)); + if (rusage) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, rusage, struct_rusage_sz); } return res; } -#define INIT_WAIT \ - INTERCEPT_FUNCTION(wait); \ - INTERCEPT_FUNCTION(waitid); \ - INTERCEPT_FUNCTION(waitpid); \ - INTERCEPT_FUNCTION(wait3); \ - INTERCEPT_FUNCTION(wait4); +#define INIT_WAIT \ + COMMON_INTERCEPT_FUNCTION(wait); \ + COMMON_INTERCEPT_FUNCTION(waitid); \ + COMMON_INTERCEPT_FUNCTION(waitpid); \ + COMMON_INTERCEPT_FUNCTION(wait3); \ + COMMON_INTERCEPT_FUNCTION(wait4); #else #define INIT_WAIT #endif @@ -969,8 +1012,7 @@ INTERCEPTOR(char *, inet_ntop, int af, const void *src, char *dst, u32 size) { if (sz) COMMON_INTERCEPTOR_READ_RANGE(ctx, src, sz); // FIXME: figure out read size based on the address family. char *res = REAL(inet_ntop)(af, src, dst, size); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { @@ -984,9 +1026,9 @@ INTERCEPTOR(int, inet_pton, int af, const char *src, void *dst) { } return res; } -#define INIT_INET \ - INTERCEPT_FUNCTION(inet_ntop); \ - INTERCEPT_FUNCTION(inet_pton); +#define INIT_INET \ + COMMON_INTERCEPT_FUNCTION(inet_ntop); \ + COMMON_INTERCEPT_FUNCTION(inet_pton); #else #define INIT_INET #endif @@ -1003,7 +1045,7 @@ INTERCEPTOR(int, inet_aton, const char *cp, void *dst) { } return res; } -#define INIT_INET_ATON INTERCEPT_FUNCTION(inet_aton); +#define INIT_INET_ATON COMMON_INTERCEPT_FUNCTION(inet_aton); #else #define INIT_INET_ATON #endif @@ -1019,7 +1061,8 @@ INTERCEPTOR(int, pthread_getschedparam, uptr thread, int *policy, int *param) { } return res; } -#define INIT_PTHREAD_GETSCHEDPARAM INTERCEPT_FUNCTION(pthread_getschedparam); +#define INIT_PTHREAD_GETSCHEDPARAM \ + COMMON_INTERCEPT_FUNCTION(pthread_getschedparam); #else #define INIT_PTHREAD_GETSCHEDPARAM #endif @@ -1051,7 +1094,7 @@ INTERCEPTOR(int, getaddrinfo, char *node, char *service, } return res; } -#define INIT_GETADDRINFO INTERCEPT_FUNCTION(getaddrinfo); +#define INIT_GETADDRINFO COMMON_INTERCEPT_FUNCTION(getaddrinfo); #else #define INIT_GETADDRINFO #endif @@ -1074,7 +1117,7 @@ INTERCEPTOR(int, getnameinfo, void *sockaddr, unsigned salen, char *host, } return res; } -#define INIT_GETNAMEINFO INTERCEPT_FUNCTION(getnameinfo); +#define INIT_GETNAMEINFO COMMON_INTERCEPT_FUNCTION(getnameinfo); #else #define INIT_GETNAMEINFO #endif @@ -1091,7 +1134,7 @@ INTERCEPTOR(int, getsockname, int sock_fd, void *addr, int *addrlen) { } return res; } -#define INIT_GETSOCKNAME INTERCEPT_FUNCTION(getsockname); +#define INIT_GETSOCKNAME COMMON_INTERCEPT_FUNCTION(getsockname); #else #define INIT_GETSOCKNAME #endif @@ -1137,10 +1180,10 @@ INTERCEPTOR(struct __sanitizer_hostent *, gethostbyaddr, void *addr, int len, return res; } -INTERCEPTOR(struct __sanitizer_hostent *, gethostent) { +INTERCEPTOR(struct __sanitizer_hostent *, gethostent, int fake) { void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, gethostent); - struct __sanitizer_hostent *res = REAL(gethostent)(); + COMMON_INTERCEPTOR_ENTER(ctx, gethostent, fake); + struct __sanitizer_hostent *res = REAL(gethostent)(fake); if (res) write_hostent(ctx, res); return res; } @@ -1152,11 +1195,11 @@ INTERCEPTOR(struct __sanitizer_hostent *, gethostbyname2, char *name, int af) { if (res) write_hostent(ctx, res); return res; } -#define INIT_GETHOSTBYNAME \ - INTERCEPT_FUNCTION(gethostent); \ - INTERCEPT_FUNCTION(gethostbyaddr); \ - INTERCEPT_FUNCTION(gethostbyname); \ - INTERCEPT_FUNCTION(gethostbyname2); +#define INIT_GETHOSTBYNAME \ + COMMON_INTERCEPT_FUNCTION(gethostent); \ + COMMON_INTERCEPT_FUNCTION(gethostbyaddr); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname2); #else #define INIT_GETHOSTBYNAME #endif @@ -1235,11 +1278,11 @@ INTERCEPTOR(int, gethostbyname2_r, char *name, int af, } return res; } -#define INIT_GETHOSTBYNAME_R \ - INTERCEPT_FUNCTION(gethostent_r); \ - INTERCEPT_FUNCTION(gethostbyaddr_r); \ - INTERCEPT_FUNCTION(gethostbyname_r); \ - INTERCEPT_FUNCTION(gethostbyname2_r); +#define INIT_GETHOSTBYNAME_R \ + COMMON_INTERCEPT_FUNCTION(gethostent_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyaddr_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname_r); \ + COMMON_INTERCEPT_FUNCTION(gethostbyname2_r); #else #define INIT_GETHOSTBYNAME_R #endif @@ -1256,7 +1299,7 @@ INTERCEPTOR(int, getsockopt, int sockfd, int level, int optname, void *optval, if (optval && optlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, optval, *optlen); return res; } -#define INIT_GETSOCKOPT INTERCEPT_FUNCTION(getsockopt); +#define INIT_GETSOCKOPT COMMON_INTERCEPT_FUNCTION(getsockopt); #else #define INIT_GETSOCKOPT #endif @@ -1272,14 +1315,13 @@ INTERCEPTOR(int, accept, int fd, void *addr, unsigned *addrlen) { } int fd2 = REAL(accept)(fd, addr, addrlen); if (fd2 >= 0) { - if (fd >= 0) - COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } -#define INIT_ACCEPT INTERCEPT_FUNCTION(accept); +#define INIT_ACCEPT COMMON_INTERCEPT_FUNCTION(accept); #else #define INIT_ACCEPT #endif @@ -1295,14 +1337,13 @@ INTERCEPTOR(int, accept4, int fd, void *addr, unsigned *addrlen, int f) { } int fd2 = REAL(accept4)(fd, addr, addrlen, f); if (fd2 >= 0) { - if (fd >= 0) - COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); + if (fd >= 0) COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, fd2); if (addr && addrlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(*addrlen, addrlen0)); } return fd2; } -#define INIT_ACCEPT4 INTERCEPT_FUNCTION(accept4); +#define INIT_ACCEPT4 COMMON_INTERCEPT_FUNCTION(accept4); #else #define INIT_ACCEPT4 #endif @@ -1335,10 +1376,10 @@ INTERCEPTOR(long double, modfl, long double x, long double *iptr) { } return res; } -#define INIT_MODF \ - INTERCEPT_FUNCTION(modf); \ - INTERCEPT_FUNCTION(modff); \ - INTERCEPT_FUNCTION(modfl); +#define INIT_MODF \ + COMMON_INTERCEPT_FUNCTION(modf); \ + COMMON_INTERCEPT_FUNCTION(modff); \ + COMMON_INTERCEPT_FUNCTION(modfl); #else #define INIT_MODF #endif @@ -1347,14 +1388,13 @@ INTERCEPTOR(long double, modfl, long double x, long double *iptr) { static void write_msghdr(void *ctx, struct __sanitizer_msghdr *msg, SSIZE_T maxlen) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg, sizeof(*msg)); - if (msg->msg_name) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, - REAL(strlen)((char *)msg->msg_name) + 1); - if (msg->msg_iov) + if (msg->msg_name && msg->msg_namelen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_name, msg->msg_namelen); + if (msg->msg_iov && msg->msg_iovlen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_iov, sizeof(*msg->msg_iov) * msg->msg_iovlen); write_iovec(ctx, msg->msg_iov, msg->msg_iovlen, maxlen); - if (msg->msg_control) + if (msg->msg_control && msg->msg_controllen) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, msg->msg_control, msg->msg_controllen); } @@ -1365,11 +1405,14 @@ INTERCEPTOR(SSIZE_T, recvmsg, int fd, struct __sanitizer_msghdr *msg, SSIZE_T res = REAL(recvmsg)(fd, msg, flags); if (res >= 0) { if (fd >= 0) COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd); - if (msg) write_msghdr(ctx, msg, res); + if (msg) { + write_msghdr(ctx, msg, res); + COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg); + } } return res; } -#define INIT_RECVMSG INTERCEPT_FUNCTION(recvmsg); +#define INIT_RECVMSG COMMON_INTERCEPT_FUNCTION(recvmsg); #else #define INIT_RECVMSG #endif @@ -1385,7 +1428,7 @@ INTERCEPTOR(int, getpeername, int sockfd, void *addr, unsigned *addrlen) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, Min(addr_sz, *addrlen)); return res; } -#define INIT_GETPEERNAME INTERCEPT_FUNCTION(getpeername); +#define INIT_GETPEERNAME COMMON_INTERCEPT_FUNCTION(getpeername); #else #define INIT_GETPEERNAME #endif @@ -1399,7 +1442,7 @@ INTERCEPTOR(int, sysinfo, void *info) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, struct_sysinfo_sz); return res; } -#define INIT_SYSINFO INTERCEPT_FUNCTION(sysinfo); +#define INIT_SYSINFO COMMON_INTERCEPT_FUNCTION(sysinfo); #else #define INIT_SYSINFO #endif @@ -1409,8 +1452,7 @@ INTERCEPTOR(__sanitizer_dirent *, readdir, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir, dirp); __sanitizer_dirent *res = REAL(readdir)(dirp); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } @@ -1427,9 +1469,9 @@ INTERCEPTOR(int, readdir_r, void *dirp, __sanitizer_dirent *entry, return res; } -#define INIT_READDIR \ - INTERCEPT_FUNCTION(readdir); \ - INTERCEPT_FUNCTION(readdir_r); +#define INIT_READDIR \ + COMMON_INTERCEPT_FUNCTION(readdir); \ + COMMON_INTERCEPT_FUNCTION(readdir_r); #else #define INIT_READDIR #endif @@ -1439,8 +1481,7 @@ INTERCEPTOR(__sanitizer_dirent64 *, readdir64, void *dirp) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, readdir64, dirp); __sanitizer_dirent64 *res = REAL(readdir64)(dirp); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, res->d_reclen); return res; } @@ -1456,9 +1497,9 @@ INTERCEPTOR(int, readdir64_r, void *dirp, __sanitizer_dirent64 *entry, } return res; } -#define INIT_READDIR64 \ - INTERCEPT_FUNCTION(readdir64); \ - INTERCEPT_FUNCTION(readdir64_r); +#define INIT_READDIR64 \ + COMMON_INTERCEPT_FUNCTION(readdir64); \ + COMMON_INTERCEPT_FUNCTION(readdir64_r); #else #define INIT_READDIR64 #endif @@ -1504,8 +1545,7 @@ INTERCEPTOR(uptr, ptrace, int request, int pid, void *addr, void *data) { return res; } -#define INIT_PTRACE \ - INTERCEPT_FUNCTION(ptrace); +#define INIT_PTRACE COMMON_INTERCEPT_FUNCTION(ptrace); #else #define INIT_PTRACE #endif @@ -1517,13 +1557,11 @@ INTERCEPTOR(char *, setlocale, int category, char *locale) { if (locale) COMMON_INTERCEPTOR_READ_RANGE(ctx, locale, REAL(strlen)(locale) + 1); char *res = REAL(setlocale)(category, locale); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } -#define INIT_SETLOCALE \ - INTERCEPT_FUNCTION(setlocale); +#define INIT_SETLOCALE COMMON_INTERCEPT_FUNCTION(setlocale); #else #define INIT_SETLOCALE #endif @@ -1533,28 +1571,25 @@ INTERCEPTOR(char *, getcwd, char *buf, SIZE_T size) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getcwd, buf, size); char *res = REAL(getcwd)(buf, size); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } -#define INIT_GETCWD \ - INTERCEPT_FUNCTION(getcwd); +#define INIT_GETCWD COMMON_INTERCEPT_FUNCTION(getcwd); #else #define INIT_GETCWD #endif #if SANITIZER_INTERCEPT_GET_CURRENT_DIR_NAME -INTERCEPTOR(char *, get_current_dir_name) { +INTERCEPTOR(char *, get_current_dir_name, int fake) { void *ctx; - COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name); - char *res = REAL(get_current_dir_name)(); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + COMMON_INTERCEPTOR_ENTER(ctx, get_current_dir_name, fake); + char *res = REAL(get_current_dir_name)(fake); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } -#define INIT_GET_CURRENT_DIR_NAME \ - INTERCEPT_FUNCTION(get_current_dir_name); +#define INIT_GET_CURRENT_DIR_NAME \ + COMMON_INTERCEPT_FUNCTION(get_current_dir_name); #else #define INIT_GET_CURRENT_DIR_NAME #endif @@ -1576,9 +1611,9 @@ INTERCEPTOR(INTMAX_T, strtoumax, const char *nptr, char **endptr, int base) { return res; } -#define INIT_STRTOIMAX \ - INTERCEPT_FUNCTION(strtoimax); \ - INTERCEPT_FUNCTION(strtoumax); +#define INIT_STRTOIMAX \ + COMMON_INTERCEPT_FUNCTION(strtoimax); \ + COMMON_INTERCEPT_FUNCTION(strtoumax); #else #define INIT_STRTOIMAX #endif @@ -1611,9 +1646,9 @@ INTERCEPTOR(SIZE_T, mbsrtowcs, wchar_t *dest, const char **src, SIZE_T len, return res; } -#define INIT_MBSTOWCS \ - INTERCEPT_FUNCTION(mbstowcs); \ - INTERCEPT_FUNCTION(mbsrtowcs); +#define INIT_MBSTOWCS \ + COMMON_INTERCEPT_FUNCTION(mbstowcs); \ + COMMON_INTERCEPT_FUNCTION(mbsrtowcs); #else #define INIT_MBSTOWCS #endif @@ -1636,7 +1671,7 @@ INTERCEPTOR(SIZE_T, mbsnrtowcs, wchar_t *dest, const char **src, SIZE_T nms, return res; } -#define INIT_MBSNRTOWCS INTERCEPT_FUNCTION(mbsnrtowcs); +#define INIT_MBSNRTOWCS COMMON_INTERCEPT_FUNCTION(mbsnrtowcs); #else #define INIT_MBSNRTOWCS #endif @@ -1667,9 +1702,9 @@ INTERCEPTOR(SIZE_T, wcsrtombs, char *dest, const wchar_t **src, SIZE_T len, return res; } -#define INIT_WCSTOMBS \ - INTERCEPT_FUNCTION(wcstombs); \ - INTERCEPT_FUNCTION(wcsrtombs); +#define INIT_WCSTOMBS \ + COMMON_INTERCEPT_FUNCTION(wcstombs); \ + COMMON_INTERCEPT_FUNCTION(wcsrtombs); #else #define INIT_WCSTOMBS #endif @@ -1692,12 +1727,11 @@ INTERCEPTOR(SIZE_T, wcsnrtombs, char *dest, const wchar_t **src, SIZE_T nms, return res; } -#define INIT_WCSNRTOMBS INTERCEPT_FUNCTION(wcsnrtombs); +#define INIT_WCSNRTOMBS COMMON_INTERCEPT_FUNCTION(wcsnrtombs); #else #define INIT_WCSNRTOMBS #endif - #if SANITIZER_INTERCEPT_TCGETATTR INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { void *ctx; @@ -1708,12 +1742,11 @@ INTERCEPTOR(int, tcgetattr, int fd, void *termios_p) { return res; } -#define INIT_TCGETATTR INTERCEPT_FUNCTION(tcgetattr); +#define INIT_TCGETATTR COMMON_INTERCEPT_FUNCTION(tcgetattr); #else #define INIT_TCGETATTR #endif - #if SANITIZER_INTERCEPT_REALPATH INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { void *ctx; @@ -1729,12 +1762,11 @@ INTERCEPTOR(char *, realpath, const char *path, char *resolved_path) { allocated_path = resolved_path = (char *)WRAP(malloc)(path_max + 1); char *res = REAL(realpath)(path, resolved_path); - if (allocated_path && !res) - WRAP(free)(allocated_path); + if (allocated_path && !res) WRAP(free)(allocated_path); if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } -#define INIT_REALPATH INTERCEPT_FUNCTION(realpath); +#define INIT_REALPATH COMMON_INTERCEPT_FUNCTION(realpath); #else #define INIT_REALPATH #endif @@ -1748,7 +1780,8 @@ INTERCEPTOR(char *, canonicalize_file_name, const char *path) { if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } -#define INIT_CANONICALIZE_FILE_NAME INTERCEPT_FUNCTION(canonicalize_file_name); +#define INIT_CANONICALIZE_FILE_NAME \ + COMMON_INTERCEPT_FUNCTION(canonicalize_file_name); #else #define INIT_CANONICALIZE_FILE_NAME #endif @@ -1762,7 +1795,7 @@ INTERCEPTOR(SIZE_T, confstr, int name, char *buf, SIZE_T len) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, res < len ? res : len); return res; } -#define INIT_CONFSTR INTERCEPT_FUNCTION(confstr); +#define INIT_CONFSTR COMMON_INTERCEPT_FUNCTION(confstr); #else #define INIT_CONFSTR #endif @@ -1772,11 +1805,10 @@ INTERCEPTOR(int, sched_getaffinity, int pid, SIZE_T cpusetsize, void *mask) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, sched_getaffinity, pid, cpusetsize, mask); int res = REAL(sched_getaffinity)(pid, cpusetsize, mask); - if (mask && !res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); + if (mask && !res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mask, cpusetsize); return res; } -#define INIT_SCHED_GETAFFINITY INTERCEPT_FUNCTION(sched_getaffinity); +#define INIT_SCHED_GETAFFINITY COMMON_INTERCEPT_FUNCTION(sched_getaffinity); #else #define INIT_SCHED_GETAFFINITY #endif @@ -1786,11 +1818,10 @@ INTERCEPTOR(char *, strerror, int errnum) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, strerror, errnum); char *res = REAL(strerror)(errnum); - if (res) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); return res; } -#define INIT_STRERROR INTERCEPT_FUNCTION(strerror); +#define INIT_STRERROR COMMON_INTERCEPT_FUNCTION(strerror); #else #define INIT_STRERROR #endif @@ -1818,11 +1849,26 @@ INTERCEPTOR(char *, strerror_r, int errnum, char *buf, SIZE_T buflen) { } return res; } -#define INIT_STRERROR_R INTERCEPT_FUNCTION(strerror_r); +#define INIT_STRERROR_R COMMON_INTERCEPT_FUNCTION(strerror_r); #else #define INIT_STRERROR_R #endif +#if SANITIZER_INTERCEPT_XPG_STRERROR_R +INTERCEPTOR(int, __xpg_strerror_r, int errnum, char *buf, SIZE_T buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, __xpg_strerror_r, errnum, buf, buflen); + int res = REAL(__xpg_strerror_r)(errnum, buf, buflen); + // This version always returns a null-terminated string. + if (buf && buflen) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + return res; +} +#define INIT_XPG_STRERROR_R COMMON_INTERCEPT_FUNCTION(__xpg_strerror_r); +#else +#define INIT_XPG_STRERROR_R +#endif + #if SANITIZER_INTERCEPT_SCANDIR typedef int (*scandir_filter_f)(const struct __sanitizer_dirent *); typedef int (*scandir_compar_f)(const struct __sanitizer_dirent **, @@ -1871,7 +1917,7 @@ INTERCEPTOR(int, scandir, char *dirp, __sanitizer_dirent ***namelist, } return res; } -#define INIT_SCANDIR INTERCEPT_FUNCTION(scandir); +#define INIT_SCANDIR COMMON_INTERCEPT_FUNCTION(scandir); #else #define INIT_SCANDIR #endif @@ -1925,7 +1971,7 @@ INTERCEPTOR(int, scandir64, char *dirp, __sanitizer_dirent64 ***namelist, } return res; } -#define INIT_SCANDIR64 INTERCEPT_FUNCTION(scandir64); +#define INIT_SCANDIR64 COMMON_INTERCEPT_FUNCTION(scandir64); #else #define INIT_SCANDIR64 #endif @@ -1935,11 +1981,10 @@ INTERCEPTOR(int, getgroups, int size, u32 *lst) { void *ctx; COMMON_INTERCEPTOR_ENTER(ctx, getgroups, size, lst); int res = REAL(getgroups)(size, lst); - if (res && lst) - COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); + if (res && lst) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lst, res * sizeof(*lst)); return res; } -#define INIT_GETGROUPS INTERCEPT_FUNCTION(getgroups); +#define INIT_GETGROUPS COMMON_INTERCEPT_FUNCTION(getgroups); #else #define INIT_GETGROUPS #endif @@ -1969,7 +2014,7 @@ INTERCEPTOR(int, poll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } -#define INIT_POLL INTERCEPT_FUNCTION(poll); +#define INIT_POLL COMMON_INTERCEPT_FUNCTION(poll); #else #define INIT_POLL #endif @@ -1988,7 +2033,7 @@ INTERCEPTOR(int, ppoll, __sanitizer_pollfd *fds, __sanitizer_nfds_t nfds, if (fds && nfds) write_pollfd(ctx, fds, nfds); return res; } -#define INIT_PPOLL INTERCEPT_FUNCTION(ppoll); +#define INIT_PPOLL COMMON_INTERCEPT_FUNCTION(ppoll); #else #define INIT_PPOLL #endif @@ -2011,7 +2056,7 @@ INTERCEPTOR(int, wordexp, char *s, __sanitizer_wordexp_t *p, int flags) { } return res; } -#define INIT_WORDEXP INTERCEPT_FUNCTION(wordexp); +#define INIT_WORDEXP COMMON_INTERCEPT_FUNCTION(wordexp); #else #define INIT_WORDEXP #endif @@ -2025,7 +2070,7 @@ INTERCEPTOR(int, sigwait, __sanitizer_sigset_t *set, int *sig) { if (!res && sig) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sig, sizeof(*sig)); return res; } -#define INIT_SIGWAIT INTERCEPT_FUNCTION(sigwait); +#define INIT_SIGWAIT COMMON_INTERCEPT_FUNCTION(sigwait); #else #define INIT_SIGWAIT #endif @@ -2039,7 +2084,7 @@ INTERCEPTOR(int, sigwaitinfo, __sanitizer_sigset_t *set, void *info) { if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } -#define INIT_SIGWAITINFO INTERCEPT_FUNCTION(sigwaitinfo); +#define INIT_SIGWAITINFO COMMON_INTERCEPT_FUNCTION(sigwaitinfo); #else #define INIT_SIGWAITINFO #endif @@ -2055,7 +2100,7 @@ INTERCEPTOR(int, sigtimedwait, __sanitizer_sigset_t *set, void *info, if (res > 0 && info) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, info, siginfo_t_sz); return res; } -#define INIT_SIGTIMEDWAIT INTERCEPT_FUNCTION(sigtimedwait); +#define INIT_SIGTIMEDWAIT COMMON_INTERCEPT_FUNCTION(sigtimedwait); #else #define INIT_SIGTIMEDWAIT #endif @@ -2076,9 +2121,9 @@ INTERCEPTOR(int, sigfillset, __sanitizer_sigset_t *set) { if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } -#define INIT_SIGSETOPS \ - INTERCEPT_FUNCTION(sigemptyset); \ - INTERCEPT_FUNCTION(sigfillset); +#define INIT_SIGSETOPS \ + COMMON_INTERCEPT_FUNCTION(sigemptyset); \ + COMMON_INTERCEPT_FUNCTION(sigfillset); #else #define INIT_SIGSETOPS #endif @@ -2091,7 +2136,7 @@ INTERCEPTOR(int, sigpending, __sanitizer_sigset_t *set) { if (!res && set) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, set, sizeof(*set)); return res; } -#define INIT_SIGPENDING INTERCEPT_FUNCTION(sigpending); +#define INIT_SIGPENDING COMMON_INTERCEPT_FUNCTION(sigpending); #else #define INIT_SIGPENDING #endif @@ -2107,7 +2152,7 @@ INTERCEPTOR(int, sigprocmask, int how, __sanitizer_sigset_t *set, COMMON_INTERCEPTOR_WRITE_RANGE(ctx, oldset, sizeof(*oldset)); return res; } -#define INIT_SIGPROCMASK INTERCEPT_FUNCTION(sigprocmask); +#define INIT_SIGPROCMASK COMMON_INTERCEPT_FUNCTION(sigprocmask); #else #define INIT_SIGPROCMASK #endif @@ -2127,7 +2172,7 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { COMMON_INTERCEPTOR_ENTER(ctx, backtrace_symbols, buffer, size); if (buffer && size) COMMON_INTERCEPTOR_READ_RANGE(ctx, buffer, size * sizeof(*buffer)); - char ** res = REAL(backtrace_symbols)(buffer, size); + char **res = REAL(backtrace_symbols)(buffer, size); if (res && size) { COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, size * sizeof(*res)); for (int i = 0; i < size; ++i) @@ -2135,13 +2180,716 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { } return res; } -#define INIT_BACKTRACE \ - INTERCEPT_FUNCTION(backtrace); \ - INTERCEPT_FUNCTION(backtrace_symbols); +#define INIT_BACKTRACE \ + COMMON_INTERCEPT_FUNCTION(backtrace); \ + COMMON_INTERCEPT_FUNCTION(backtrace_symbols); #else #define INIT_BACKTRACE #endif +#if SANITIZER_INTERCEPT__EXIT +INTERCEPTOR(void, _exit, int status) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, _exit, status); + int status1 = COMMON_INTERCEPTOR_ON_EXIT(ctx); + if (status == 0) status = status1; + REAL(_exit)(status); +} +#define INIT__EXIT COMMON_INTERCEPT_FUNCTION(_exit); +#else +#define INIT__EXIT +#endif + +#if SANITIZER_INTERCEPT_PHTREAD_MUTEX +INTERCEPTOR(int, pthread_mutex_lock, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_lock, m); + int res = REAL(pthread_mutex_lock)(m); + if (res == errno_EOWNERDEAD) + COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m); + if (res == 0 || res == errno_EOWNERDEAD) + COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + return res; +} + +INTERCEPTOR(int, pthread_mutex_unlock, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_mutex_unlock, m); + COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); + return REAL(pthread_mutex_unlock)(m); +} + +#define INIT_PTHREAD_MUTEX_LOCK COMMON_INTERCEPT_FUNCTION(pthread_mutex_lock) +#define INIT_PTHREAD_MUTEX_UNLOCK \ + COMMON_INTERCEPT_FUNCTION(pthread_mutex_unlock) +#else +#define INIT_PTHREAD_MUTEX_LOCK +#define INIT_PTHREAD_MUTEX_UNLOCK +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_COND +INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_wait, c, m); + COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + int res = REAL(pthread_cond_wait)(c, m); + COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m); + return res; +} + +INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_init, c, a); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_init)(c, a); +} + +INTERCEPTOR(int, pthread_cond_signal, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_signal, c); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_signal)(c); +} + +INTERCEPTOR(int, pthread_cond_broadcast, void *c) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_cond_broadcast, c); + COMMON_INTERCEPTOR_READ_RANGE(ctx, c, pthread_cond_t_sz); + return REAL(pthread_cond_broadcast)(c); +} + +#define INIT_PTHREAD_COND_WAIT \ + INTERCEPT_FUNCTION_VER(pthread_cond_wait, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_INIT \ + INTERCEPT_FUNCTION_VER(pthread_cond_init, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_SIGNAL \ + INTERCEPT_FUNCTION_VER(pthread_cond_signal, "GLIBC_2.3.2") +#define INIT_PTHREAD_COND_BROADCAST \ + INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, "GLIBC_2.3.2") +#else +#define INIT_PTHREAD_COND_WAIT +#define INIT_PTHREAD_COND_INIT +#define INIT_PTHREAD_COND_SIGNAL +#define INIT_PTHREAD_COND_BROADCAST +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT || SANITIZER_INTERCEPT_GETMNTENT_R +static void write_mntent(void *ctx, __sanitizer_mntent *mnt) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt, sizeof(*mnt)); + if (mnt->mnt_fsname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_fsname, + REAL(strlen)(mnt->mnt_fsname) + 1); + if (mnt->mnt_dir) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_dir, + REAL(strlen)(mnt->mnt_dir) + 1); + if (mnt->mnt_type) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_type, + REAL(strlen)(mnt->mnt_type) + 1); + if (mnt->mnt_opts) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, mnt->mnt_opts, + REAL(strlen)(mnt->mnt_opts) + 1); +} +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT +INTERCEPTOR(__sanitizer_mntent *, getmntent, void *fp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getmntent, fp); + __sanitizer_mntent *res = REAL(getmntent)(fp); + if (res) write_mntent(ctx, res); + return res; +} +#define INIT_GETMNTENT COMMON_INTERCEPT_FUNCTION(getmntent); +#else +#define INIT_GETMNTENT +#endif + +#if SANITIZER_INTERCEPT_GETMNTENT_R +INTERCEPTOR(__sanitizer_mntent *, getmntent_r, void *fp, + __sanitizer_mntent *mntbuf, char *buf, int buflen) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getmntent_r, fp, mntbuf, buf, buflen); + __sanitizer_mntent *res = REAL(getmntent_r)(fp, mntbuf, buf, buflen); + if (res) write_mntent(ctx, res); + return res; +} +#define INIT_GETMNTENT_R COMMON_INTERCEPT_FUNCTION(getmntent_r); +#else +#define INIT_GETMNTENT_R +#endif + +#if SANITIZER_INTERCEPT_STATFS +INTERCEPTOR(int, statfs, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statfs, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statfs)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); + return res; +} +INTERCEPTOR(int, fstatfs, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatfs, fd, buf); + int res = REAL(fstatfs)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs_sz); + return res; +} +#define INIT_STATFS \ + COMMON_INTERCEPT_FUNCTION(statfs); \ + COMMON_INTERCEPT_FUNCTION(fstatfs); +#else +#define INIT_STATFS +#endif + +#if SANITIZER_INTERCEPT_STATFS64 +INTERCEPTOR(int, statfs64, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statfs64, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statfs64)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); + return res; +} +INTERCEPTOR(int, fstatfs64, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatfs64, fd, buf); + int res = REAL(fstatfs64)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statfs64_sz); + return res; +} +#define INIT_STATFS64 \ + COMMON_INTERCEPT_FUNCTION(statfs64); \ + COMMON_INTERCEPT_FUNCTION(fstatfs64); +#else +#define INIT_STATFS64 +#endif + +#if SANITIZER_INTERCEPT_STATVFS +INTERCEPTOR(int, statvfs, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statvfs, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statvfs)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); + return res; +} +INTERCEPTOR(int, fstatvfs, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs, fd, buf); + int res = REAL(fstatvfs)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs_sz); + return res; +} +#define INIT_STATVFS \ + COMMON_INTERCEPT_FUNCTION(statvfs); \ + COMMON_INTERCEPT_FUNCTION(fstatvfs); +#else +#define INIT_STATVFS +#endif + +#if SANITIZER_INTERCEPT_STATVFS64 +INTERCEPTOR(int, statvfs64, char *path, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, statvfs64, path, buf); + if (path) COMMON_INTERCEPTOR_READ_RANGE(ctx, path, REAL(strlen)(path) + 1); + int res = REAL(statvfs64)(path, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); + return res; +} +INTERCEPTOR(int, fstatvfs64, int fd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, fstatvfs64, fd, buf); + int res = REAL(fstatvfs64)(fd, buf); + if (!res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, struct_statvfs64_sz); + return res; +} +#define INIT_STATVFS64 \ + COMMON_INTERCEPT_FUNCTION(statvfs64); \ + COMMON_INTERCEPT_FUNCTION(fstatvfs64); +#else +#define INIT_STATVFS64 +#endif + +#if SANITIZER_INTERCEPT_INITGROUPS +INTERCEPTOR(int, initgroups, char *user, u32 group) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, initgroups, user, group); + if (user) COMMON_INTERCEPTOR_READ_RANGE(ctx, user, REAL(strlen)(user) + 1); + int res = REAL(initgroups)(user, group); + return res; +} +#define INIT_INITGROUPS COMMON_INTERCEPT_FUNCTION(initgroups); +#else +#define INIT_INITGROUPS +#endif + +#if SANITIZER_INTERCEPT_ETHER +INTERCEPTOR(char *, ether_ntoa, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa, addr); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + char *res = REAL(ether_ntoa)(addr); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(__sanitizer_ether_addr *, ether_aton, char *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_aton, buf); + if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + __sanitizer_ether_addr *res = REAL(ether_aton)(buf); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, sizeof(*res)); + return res; +} +INTERCEPTOR(int, ether_ntohost, char *hostname, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntohost, hostname, addr); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + int res = REAL(ether_ntohost)(hostname, addr); + if (!res && hostname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + return res; +} +INTERCEPTOR(int, ether_hostton, char *hostname, __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_hostton, hostname, addr); + if (hostname) + COMMON_INTERCEPTOR_READ_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + int res = REAL(ether_hostton)(hostname, addr); + if (!res && addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + return res; +} +INTERCEPTOR(int, ether_line, char *line, __sanitizer_ether_addr *addr, + char *hostname) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_line, line, addr, hostname); + if (line) COMMON_INTERCEPTOR_READ_RANGE(ctx, line, REAL(strlen)(line) + 1); + int res = REAL(ether_line)(line, addr, hostname); + if (!res) { + if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + if (hostname) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, hostname, REAL(strlen)(hostname) + 1); + } + return res; +} +#define INIT_ETHER \ + COMMON_INTERCEPT_FUNCTION(ether_ntoa); \ + COMMON_INTERCEPT_FUNCTION(ether_aton); \ + COMMON_INTERCEPT_FUNCTION(ether_ntohost); \ + COMMON_INTERCEPT_FUNCTION(ether_hostton); \ + COMMON_INTERCEPT_FUNCTION(ether_line); +#else +#define INIT_ETHER +#endif + +#if SANITIZER_INTERCEPT_ETHER_R +INTERCEPTOR(char *, ether_ntoa_r, __sanitizer_ether_addr *addr, char *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_ntoa_r, addr, buf); + if (addr) COMMON_INTERCEPTOR_READ_RANGE(ctx, addr, sizeof(*addr)); + char *res = REAL(ether_ntoa_r)(addr, buf); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +INTERCEPTOR(__sanitizer_ether_addr *, ether_aton_r, char *buf, + __sanitizer_ether_addr *addr) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, ether_aton_r, buf, addr); + if (buf) COMMON_INTERCEPTOR_READ_RANGE(ctx, buf, REAL(strlen)(buf) + 1); + __sanitizer_ether_addr *res = REAL(ether_aton_r)(buf, addr); + if (res) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, res, sizeof(*res)); + return res; +} +#define INIT_ETHER_R \ + COMMON_INTERCEPT_FUNCTION(ether_ntoa_r); \ + COMMON_INTERCEPT_FUNCTION(ether_aton_r); +#else +#define INIT_ETHER_R +#endif + +#if SANITIZER_INTERCEPT_SHMCTL +INTERCEPTOR(int, shmctl, int shmid, int cmd, void *buf) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, shmctl, shmid, cmd, buf); + int res = REAL(shmctl)(shmid, cmd, buf); + if (res >= 0) { + unsigned sz = 0; + if (cmd == shmctl_ipc_stat || cmd == shmctl_shm_stat) + sz = sizeof(__sanitizer_shmid_ds); + else if (cmd == shmctl_ipc_info) + sz = struct_shminfo_sz; + else if (cmd == shmctl_shm_info) + sz = struct_shm_info_sz; + if (sz) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, buf, sz); + } + return res; +} +#define INIT_SHMCTL COMMON_INTERCEPT_FUNCTION(shmctl); +#else +#define INIT_SHMCTL +#endif + +#if SANITIZER_INTERCEPT_RANDOM_R +INTERCEPTOR(int, random_r, void *buf, u32 *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, random_r, buf, result); + int res = REAL(random_r)(buf, result); + if (!res && result) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +#define INIT_RANDOM_R COMMON_INTERCEPT_FUNCTION(random_r); +#else +#define INIT_RANDOM_R +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET || \ + SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSSCHED +#define INTERCEPTOR_PTHREAD_ATTR_GET(what, sz) \ + INTERCEPTOR(int, pthread_attr_get##what, void *attr, void *r) { \ + void *ctx; \ + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_get##what, attr, r); \ + int res = REAL(pthread_attr_get##what)(attr, r); \ + if (!res && r) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, r, sz); \ + return res; \ + } +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GET +INTERCEPTOR_PTHREAD_ATTR_GET(detachstate, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(guardsize, sizeof(SIZE_T)) +INTERCEPTOR_PTHREAD_ATTR_GET(schedparam, struct_sched_param_sz) +INTERCEPTOR_PTHREAD_ATTR_GET(schedpolicy, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(scope, sizeof(int)) +INTERCEPTOR_PTHREAD_ATTR_GET(stacksize, sizeof(SIZE_T)) +INTERCEPTOR(int, pthread_attr_getstack, void *attr, void **addr, SIZE_T *size) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getstack, attr, addr, size); + int res = REAL(pthread_attr_getstack)(attr, addr, size); + if (!res) { + if (addr) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, addr, sizeof(*addr)); + if (size) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, size, sizeof(*size)); + } + return res; +} + +// We may need to call the real pthread_attr_getstack from the run-time +// in sanitizer_common, but we don't want to include the interception headers +// there. So, just define this function here. +int __sanitizer_pthread_attr_getstack(void *attr, void **addr, SIZE_T *size) { + return REAL(pthread_attr_getstack)(attr, addr, size); +} + +#define INIT_PTHREAD_ATTR_GET \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getdetachstate); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getguardsize); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedparam); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getschedpolicy); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getscope); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getstacksize); \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getstack); +#else +#define INIT_PTHREAD_ATTR_GET +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED +INTERCEPTOR_PTHREAD_ATTR_GET(inheritsched, sizeof(int)) + +#define INIT_PTHREAD_ATTR_GETINHERITSCHED \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getinheritsched); +#else +#define INIT_PTHREAD_ATTR_GETINHERITSCHED +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP +INTERCEPTOR(int, pthread_attr_getaffinity_np, void *attr, SIZE_T cpusetsize, + void *cpuset) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_attr_getaffinity_np, attr, cpusetsize, + cpuset); + int res = REAL(pthread_attr_getaffinity_np)(attr, cpusetsize, cpuset); + if (!res && cpusetsize && cpuset) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cpuset, cpusetsize); + return res; +} + +#define INIT_PTHREAD_ATTR_GETAFFINITY_NP \ + COMMON_INTERCEPT_FUNCTION(pthread_attr_getaffinity_np); +#else +#define INIT_PTHREAD_ATTR_GETAFFINITY_NP +#endif + +#if SANITIZER_INTERCEPT_TMPNAM +INTERCEPTOR(char *, tmpnam, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tmpnam, s); + char *res = REAL(tmpnam)(s); + if (res) { + if (s) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); + else + COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + } + return res; +} +#define INIT_TMPNAM COMMON_INTERCEPT_FUNCTION(tmpnam); +#else +#define INIT_TMPNAM +#endif + +#if SANITIZER_INTERCEPT_TMPNAM_R +INTERCEPTOR(char *, tmpnam_r, char *s) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tmpnam_r, s); + char *res = REAL(tmpnam_r)(s); + if (res && s) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, s, REAL(strlen)(s) + 1); + return res; +} +#define INIT_TMPNAM_R COMMON_INTERCEPT_FUNCTION(tmpnam_r); +#else +#define INIT_TMPNAM_R +#endif + +#if SANITIZER_INTERCEPT_TEMPNAM +INTERCEPTOR(char *, tempnam, char *dir, char *pfx) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, tempnam, dir, pfx); + if (dir) COMMON_INTERCEPTOR_READ_RANGE(ctx, dir, REAL(strlen)(dir) + 1); + if (pfx) COMMON_INTERCEPTOR_READ_RANGE(ctx, pfx, REAL(strlen)(pfx) + 1); + char *res = REAL(tempnam)(dir, pfx); + if (res) COMMON_INTERCEPTOR_INITIALIZE_RANGE(ctx, res, REAL(strlen)(res) + 1); + return res; +} +#define INIT_TEMPNAM COMMON_INTERCEPT_FUNCTION(tempnam); +#else +#define INIT_TEMPNAM +#endif + +#if SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP +INTERCEPTOR(int, pthread_setname_np, uptr thread, const char *name) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, pthread_setname_np, thread, name); + COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name); + return REAL(pthread_setname_np)(thread, name); +} +#define INIT_PTHREAD_SETNAME_NP COMMON_INTERCEPT_FUNCTION(pthread_setname_np); +#else +#define INIT_PTHREAD_SETNAME_NP +#endif + +#if SANITIZER_INTERCEPT_SINCOS +INTERCEPTOR(void, sincos, double x, double *sin, double *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincos, x, sin, cos); + REAL(sincos)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +INTERCEPTOR(void, sincosf, float x, float *sin, float *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincosf, x, sin, cos); + REAL(sincosf)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +INTERCEPTOR(void, sincosl, long double x, long double *sin, long double *cos) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, sincosl, x, sin, cos); + REAL(sincosl)(x, sin, cos); + if (sin) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, sin, sizeof(*sin)); + if (cos) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, cos, sizeof(*cos)); +} +#define INIT_SINCOS \ + COMMON_INTERCEPT_FUNCTION(sincos); \ + COMMON_INTERCEPT_FUNCTION(sincosf); \ + COMMON_INTERCEPT_FUNCTION(sincosl); +#else +#define INIT_SINCOS +#endif + +#if SANITIZER_INTERCEPT_REMQUO +INTERCEPTOR(double, remquo, double x, double y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquo, x, y, quo); + double res = REAL(remquo)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +INTERCEPTOR(float, remquof, float x, float y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquof, x, y, quo); + float res = REAL(remquof)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +INTERCEPTOR(long double, remquol, long double x, long double y, int *quo) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, remquol, x, y, quo); + long double res = REAL(remquol)(x, y, quo); + if (quo) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, quo, sizeof(*quo)); + return res; +} +#define INIT_REMQUO \ + COMMON_INTERCEPT_FUNCTION(remquo); \ + COMMON_INTERCEPT_FUNCTION(remquof); \ + COMMON_INTERCEPT_FUNCTION(remquol); +#else +#define INIT_REMQUO +#endif + +#if SANITIZER_INTERCEPT_LGAMMA +extern int signgam; +INTERCEPTOR(double, lgamma, double x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgamma, x); + double res = REAL(lgamma)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +INTERCEPTOR(float, lgammaf, float x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammaf, x); + float res = REAL(lgammaf)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +INTERCEPTOR(long double, lgammal, long double x) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammal, x); + long double res = REAL(lgammal)(x); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, &signgam, sizeof(signgam)); + return res; +} +#define INIT_LGAMMA \ + COMMON_INTERCEPT_FUNCTION(lgamma); \ + COMMON_INTERCEPT_FUNCTION(lgammaf); \ + COMMON_INTERCEPT_FUNCTION(lgammal); +#else +#define INIT_LGAMMA +#endif + +#if SANITIZER_INTERCEPT_LGAMMA_R +INTERCEPTOR(double, lgamma_r, double x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgamma_r, x, signp); + double res = REAL(lgamma_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +INTERCEPTOR(float, lgammaf_r, float x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammaf_r, x, signp); + float res = REAL(lgammaf_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +INTERCEPTOR(long double, lgammal_r, long double x, int *signp) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lgammal_r, x, signp); + long double res = REAL(lgammal_r)(x, signp); + if (signp) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, signp, sizeof(*signp)); + return res; +} +#define INIT_LGAMMA_R \ + COMMON_INTERCEPT_FUNCTION(lgamma_r); \ + COMMON_INTERCEPT_FUNCTION(lgammaf_r); \ + COMMON_INTERCEPT_FUNCTION(lgammal_r); +#else +#define INIT_LGAMMA_R +#endif + +#if SANITIZER_INTERCEPT_DRAND48_R +INTERCEPTOR(int, drand48_r, void *buffer, double *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, drand48_r, buffer, result); + int res = REAL(drand48_r)(buffer, result); + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +INTERCEPTOR(int, lrand48_r, void *buffer, long *result) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, lrand48_r, buffer, result); + int res = REAL(lrand48_r)(buffer, result); + if (result) COMMON_INTERCEPTOR_WRITE_RANGE(ctx, result, sizeof(*result)); + return res; +} +#define INIT_DRAND48_R \ + COMMON_INTERCEPT_FUNCTION(drand48_r); \ + COMMON_INTERCEPT_FUNCTION(lrand48_r); +#else +#define INIT_DRAND48_R +#endif + +#if SANITIZER_INTERCEPT_GETLINE +INTERCEPTOR(SSIZE_T, getline, char **lineptr, SIZE_T *n, void *stream) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getline, lineptr, n, stream); + SSIZE_T res = REAL(getline)(lineptr, n, stream); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + } + return res; +} +INTERCEPTOR(SSIZE_T, getdelim, char **lineptr, SIZE_T *n, int delim, + void *stream) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, getdelim, lineptr, n, delim, stream); + SSIZE_T res = REAL(getdelim)(lineptr, n, delim, stream); + if (res > 0) { + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, lineptr, sizeof(*lineptr)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, n, sizeof(*n)); + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, *lineptr, res + 1); + } + return res; +} +#define INIT_GETLINE \ + COMMON_INTERCEPT_FUNCTION(getline); \ + COMMON_INTERCEPT_FUNCTION(getdelim); +#else +#define INIT_GETLINE +#endif + +#if SANITIZER_INTERCEPT_ICONV +INTERCEPTOR(SIZE_T, iconv, void *cd, char **inbuf, SIZE_T *inbytesleft, + char **outbuf, SIZE_T *outbytesleft) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, iconv, cd, inbuf, inbytesleft, outbuf, + outbytesleft); + if (inbytesleft) + COMMON_INTERCEPTOR_READ_RANGE(ctx, inbytesleft, sizeof(*inbytesleft)); + if (inbuf && inbytesleft) + COMMON_INTERCEPTOR_READ_RANGE(ctx, *inbuf, *inbytesleft); + if (outbytesleft) + COMMON_INTERCEPTOR_READ_RANGE(ctx, outbytesleft, sizeof(*outbytesleft)); + void *outbuf_orig = outbuf ? *outbuf : 0; + SIZE_T res = REAL(iconv)(cd, inbuf, inbytesleft, outbuf, outbytesleft); + if (res != (SIZE_T) - 1 && outbuf && *outbuf > outbuf_orig) { + SIZE_T sz = (char *)*outbuf - (char *)outbuf_orig; + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, outbuf_orig, sz); + } + return res; +} +#define INIT_ICONV COMMON_INTERCEPT_FUNCTION(iconv); +#else +#define INIT_ICONV +#endif + +#if SANITIZER_INTERCEPT_TIMES +INTERCEPTOR(__sanitizer_clock_t, times, void *tms) { + void *ctx; + COMMON_INTERCEPTOR_ENTER(ctx, times, tms); + __sanitizer_clock_t res = REAL(times)(tms); + if (res != (__sanitizer_clock_t)-1 && tms) + COMMON_INTERCEPTOR_WRITE_RANGE(ctx, tms, struct_tms_sz); + return res; +} +#define INIT_TIMES COMMON_INTERCEPT_FUNCTION(times); +#else +#define INIT_TIMES +#endif + #define SANITIZER_COMMON_INTERCEPTORS_INIT \ INIT_STRCMP; \ INIT_STRNCMP; \ @@ -2161,6 +2909,7 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { INIT_PWRITEV64; \ INIT_PRCTL; \ INIT_LOCALTIME_AND_FRIENDS; \ + INIT_STRPTIME; \ INIT_SCANF; \ INIT_ISOC99_SCANF; \ INIT_FREXP; \ @@ -2206,6 +2955,7 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { INIT_SCHED_GETAFFINITY; \ INIT_STRERROR; \ INIT_STRERROR_R; \ + INIT_XPG_STRERROR_R; \ INIT_SCANDIR; \ INIT_SCANDIR64; \ INIT_GETGROUPS; \ @@ -2218,4 +2968,38 @@ INTERCEPTOR(char **, backtrace_symbols, void **buffer, int size) { INIT_SIGSETOPS; \ INIT_SIGPENDING; \ INIT_SIGPROCMASK; \ - INIT_BACKTRACE; + INIT_BACKTRACE; \ + INIT__EXIT; \ + INIT_PTHREAD_MUTEX_LOCK; \ + INIT_PTHREAD_MUTEX_UNLOCK; \ + INIT_PTHREAD_COND_WAIT; \ + INIT_PTHREAD_COND_INIT; \ + INIT_PTHREAD_COND_SIGNAL; \ + INIT_PTHREAD_COND_BROADCAST; \ + INIT_GETMNTENT; \ + INIT_GETMNTENT_R; \ + INIT_STATFS; \ + INIT_STATFS64; \ + INIT_STATVFS; \ + INIT_STATVFS64; \ + INIT_INITGROUPS; \ + INIT_ETHER; \ + INIT_ETHER_R; \ + INIT_SHMCTL; \ + INIT_RANDOM_R; \ + INIT_PTHREAD_ATTR_GET; \ + INIT_PTHREAD_ATTR_GETINHERITSCHED; \ + INIT_PTHREAD_ATTR_GETAFFINITY_NP; \ + INIT_TMPNAM; \ + INIT_TMPNAM_R; \ + INIT_TEMPNAM; \ + INIT_PTHREAD_SETNAME_NP; \ + INIT_SINCOS; \ + INIT_REMQUO; \ + INIT_LGAMMA; \ + INIT_LGAMMA_R; \ + INIT_DRAND48_R; \ + INIT_GETLINE; \ + INIT_ICONV; \ + INIT_TIMES; \ +/**/ diff --git a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc index 50db0d7..ac8cdae 100755 --- a/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_interceptors_ioctl.inc @@ -86,7 +86,7 @@ static void ioctl_table_fill() { _(TIOCSTI, READ, sizeof(char)); _(TIOCSWINSZ, READ, struct_winsize_sz); -#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_MAC +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) _(SIOCGETSGCNT, WRITE, struct_sioc_sg_req_sz); _(SIOCGETVIFCNT, WRITE, struct_sioc_vif_req_sz); #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc index ae6a630..75f7d1d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc +++ b/libsanitizer/sanitizer_common/sanitizer_common_syscalls.inc @@ -23,8 +23,16 @@ // COMMON_SYSCALL_POST_WRITE_RANGE // Called in posthook for regions that were written to by the kernel // and are now initialized. +// COMMON_SYSCALL_ACQUIRE(addr) +// Acquire memory visibility from addr. +// COMMON_SYSCALL_RELEASE(addr) +// Release memory visibility to addr. // COMMON_SYSCALL_FD_CLOSE(fd) // Called before closing file descriptor fd. +// COMMON_SYSCALL_FD_ACQUIRE(fd) +// Acquire memory visibility from fd. +// COMMON_SYSCALL_FD_RELEASE(fd) +// Release memory visibility to fd. // COMMON_SYSCALL_PRE_FORK() // Called before fork syscall. // COMMON_SYSCALL_POST_FORK(long res) @@ -46,20 +54,34 @@ #define POST_READ(p, s) COMMON_SYSCALL_POST_READ_RANGE(p, s) #define POST_WRITE(p, s) COMMON_SYSCALL_POST_WRITE_RANGE(p, s) +#ifndef COMMON_SYSCALL_ACQUIRE +# define COMMON_SYSCALL_ACQUIRE(addr) ((void)(addr)) +#endif + +#ifndef COMMON_SYSCALL_RELEASE +# define COMMON_SYSCALL_RELEASE(addr) ((void)(addr)) +#endif + #ifndef COMMON_SYSCALL_FD_CLOSE -# define COMMON_SYSCALL_FD_CLOSE(fd) +# define COMMON_SYSCALL_FD_CLOSE(fd) ((void)(fd)) +#endif + +#ifndef COMMON_SYSCALL_FD_ACQUIRE +# define COMMON_SYSCALL_FD_ACQUIRE(fd) ((void)(fd)) +#endif + +#ifndef COMMON_SYSCALL_FD_RELEASE +# define COMMON_SYSCALL_FD_RELEASE(fd) ((void)(fd)) #endif #ifndef COMMON_SYSCALL_PRE_FORK -# define COMMON_SYSCALL_PRE_FORK() +# define COMMON_SYSCALL_PRE_FORK() {} #endif #ifndef COMMON_SYSCALL_POST_FORK -# define COMMON_SYSCALL_POST_FORK(res) +# define COMMON_SYSCALL_POST_FORK(res) {} #endif -#ifdef SYSCALL_INTERCEPTION - // FIXME: do some kind of PRE_READ for all syscall arguments (int(s) and such). extern "C" { @@ -1245,11 +1267,17 @@ PRE_SYSCALL(flock)(long fd, long cmd) {} POST_SYSCALL(flock)(long res, long fd, long cmd) {} -PRE_SYSCALL(io_setup)(long nr_reqs, void *ctx) {} +PRE_SYSCALL(io_setup)(long nr_reqs, void **ctx) { + if (ctx) PRE_WRITE(ctx, sizeof(*ctx)); +} -POST_SYSCALL(io_setup)(long res, long nr_reqs, void *ctx) { +POST_SYSCALL(io_setup)(long res, long nr_reqs, void **ctx) { if (res >= 0) { - if (ctx) POST_WRITE(ctx, sizeof(long)); + if (ctx) POST_WRITE(ctx, sizeof(*ctx)); + // (*ctx) is actually a pointer to a kernel mapped page, and there are + // people out there who are crazy enough to peek into that page's 32-byte + // header. + if (*ctx) POST_WRITE(*ctx, 32); } } @@ -1257,29 +1285,70 @@ PRE_SYSCALL(io_destroy)(long ctx) {} POST_SYSCALL(io_destroy)(long res, long ctx) {} -PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr, void *events, - void *timeout) { +PRE_SYSCALL(io_getevents)(long ctx_id, long min_nr, long nr, + __sanitizer_io_event *ioevpp, void *timeout) { if (timeout) PRE_READ(timeout, struct_timespec_sz); } POST_SYSCALL(io_getevents)(long res, long ctx_id, long min_nr, long nr, - void *events, void *timeout) { + __sanitizer_io_event *ioevpp, void *timeout) { if (res >= 0) { - if (events) POST_WRITE(events, res * struct_io_event_sz); + if (ioevpp) POST_WRITE(ioevpp, res * sizeof(*ioevpp)); if (timeout) POST_WRITE(timeout, struct_timespec_sz); } + for (long i = 0; i < res; i++) { + // We synchronize io_submit -> io_getevents/io_cancel using the + // user-provided data context. Data is not necessary a pointer, it can be + // an int, 0 or whatever; acquire/release will correctly handle this. + // This scheme can lead to false negatives, e.g. when all operations + // synchronize on 0. But there does not seem to be a better solution + // (except wrapping all operations in own context, which is unreliable). + // We can not reliably extract fildes in io_getevents. + COMMON_SYSCALL_ACQUIRE((void*)ioevpp[i].data); + } +} + +PRE_SYSCALL(io_submit)(long ctx_id, long nr, __sanitizer_iocb **iocbpp) { + for (long i = 0; i < nr; ++i) { + uptr op = iocbpp[i]->aio_lio_opcode; + void *data = (void*)iocbpp[i]->aio_data; + void *buf = (void*)iocbpp[i]->aio_buf; + uptr len = (uptr)iocbpp[i]->aio_nbytes; + if (op == iocb_cmd_pwrite && buf && len) { + PRE_READ(buf, len); + } else if (op == iocb_cmd_pread && buf && len) { + POST_WRITE(buf, len); + } else if (op == iocb_cmd_pwritev) { + __sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf; + for (uptr v = 0; v < len; v++) + PRE_READ(iovec[i].iov_base, iovec[i].iov_len); + } else if (op == iocb_cmd_preadv) { + __sanitizer_iovec *iovec = (__sanitizer_iovec*)iocbpp[i]->aio_buf; + for (uptr v = 0; v < len; v++) + POST_WRITE(iovec[i].iov_base, iovec[i].iov_len); + } + // See comment in io_getevents. + COMMON_SYSCALL_RELEASE(data); + } } -PRE_SYSCALL(io_submit)(long, long arg1, void *arg2) {} - -POST_SYSCALL(io_submit)(long res, long, long arg1, void *arg2) {} +POST_SYSCALL(io_submit)(long res, long ctx_id, long nr, + __sanitizer_iocb **iocbpp) {} -PRE_SYSCALL(io_cancel)(long ctx_id, void *iocb, void *result) {} +PRE_SYSCALL(io_cancel)(long ctx_id, __sanitizer_iocb *iocb, + __sanitizer_io_event *result) { +} -POST_SYSCALL(io_cancel)(long res, long ctx_id, void *iocb, void *result) { - if (res >= 0) { - if (iocb) POST_WRITE(iocb, struct_iocb_sz); - if (result) POST_WRITE(result, struct_io_event_sz); +POST_SYSCALL(io_cancel)(long res, long ctx_id, __sanitizer_iocb *iocb, + __sanitizer_io_event *result) { + if (res == 0) { + if (result) { + // See comment in io_getevents. + COMMON_SYSCALL_ACQUIRE((void*)result->data); + POST_WRITE(result, sizeof(*result)); + } + if (iocb) + POST_WRITE(iocb, sizeof(*iocb)); } } @@ -2063,14 +2132,6 @@ POST_SYSCALL(shmdt)(long res, void *shmaddr) { } } -PRE_SYSCALL(shmctl)(long shmid, long cmd, void *buf) {} - -POST_SYSCALL(shmctl)(long res, long shmid, long cmd, void *buf) { - if (res >= 0) { - if (buf) POST_WRITE(buf, struct_shmid_ds_sz); - } -} - PRE_SYSCALL(ipc)(long call, long first, long second, long third, void *ptr, long fifth) {} @@ -2078,6 +2139,14 @@ POST_SYSCALL(ipc)(long res, long call, long first, long second, long third, void *ptr, long fifth) {} #if !SANITIZER_ANDROID +PRE_SYSCALL(shmctl)(long shmid, long cmd, void *buf) {} + +POST_SYSCALL(shmctl)(long res, long shmid, long cmd, void *buf) { + if (res >= 0) { + if (buf) POST_WRITE(buf, sizeof(__sanitizer_shmid_ds)); + } +} + PRE_SYSCALL(mq_open)(const void *name, long oflag, long mode, void *attr) { if (name) PRE_READ(name, __sanitizer::internal_strlen((const char *)name) + 1); @@ -2218,9 +2287,49 @@ PRE_SYSCALL(ni_syscall)() {} POST_SYSCALL(ni_syscall)(long res) {} -PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) {} +PRE_SYSCALL(ptrace)(long request, long pid, long addr, long data) { +#if defined(__i386) || defined (__x86_64) + if (data) { + if (request == ptrace_setregs) { + PRE_READ((void *)data, struct_user_regs_struct_sz); + } else if (request == ptrace_setfpregs) { + PRE_READ((void *)data, struct_user_fpregs_struct_sz); + } else if (request == ptrace_setfpxregs) { + PRE_READ((void *)data, struct_user_fpxregs_struct_sz); + } else if (request == ptrace_setsiginfo) { + PRE_READ((void *)data, siginfo_t_sz); + } else if (request == ptrace_setregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + PRE_READ(iov->iov_base, iov->iov_len); + } + } +#endif +} -POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) {} +POST_SYSCALL(ptrace)(long res, long request, long pid, long addr, long data) { +#if defined(__i386) || defined (__x86_64) + if (res >= 0 && data) { + // Note that this is different from the interceptor in + // sanitizer_common_interceptors.inc. + // PEEK* requests return resulting values through data pointer. + if (request == ptrace_getregs) { + POST_WRITE((void *)data, struct_user_regs_struct_sz); + } else if (request == ptrace_getfpregs) { + POST_WRITE((void *)data, struct_user_fpregs_struct_sz); + } else if (request == ptrace_getfpxregs) { + POST_WRITE((void *)data, struct_user_fpxregs_struct_sz); + } else if (request == ptrace_getsiginfo) { + POST_WRITE((void *)data, siginfo_t_sz); + } else if (request == ptrace_getregset) { + __sanitizer_iovec *iov = (__sanitizer_iovec *)data; + POST_WRITE(iov->iov_base, iov->iov_len); + } else if (request == ptrace_peekdata || request == ptrace_peektext || + request == ptrace_peekuser) { + POST_WRITE((void *)data, sizeof(void *)); + } + } +#endif +} PRE_SYSCALL(add_key)(const void *_type, const void *_description, const void *_payload, long plen, long destringid) { @@ -2648,16 +2757,14 @@ PRE_SYSCALL(syncfs)(long fd) {} POST_SYSCALL(syncfs)(long res, long fd) {} -PRE_SYSCALL(perf_event_open)(void *attr_uptr, long pid, long cpu, long group_fd, - long flags) {} - -POST_SYSCALL(perf_event_open)(long res, void *attr_uptr, long pid, long cpu, - long group_fd, long flags) { - if (res >= 0) { - if (attr_uptr) POST_WRITE(attr_uptr, struct_perf_event_attr_sz); - } +PRE_SYSCALL(perf_event_open)(__sanitizer_perf_event_attr *attr_uptr, long pid, + long cpu, long group_fd, long flags) { + if (attr_uptr) PRE_READ(attr_uptr, attr_uptr->size); } +POST_SYSCALL(perf_event_open)(long res, __sanitizer_perf_event_attr *attr_uptr, + long pid, long cpu, long group_fd, long flags) {} + PRE_SYSCALL(mmap_pgoff)(long addr, long len, long prot, long flags, long fd, long pgoff) {} @@ -2724,8 +2831,6 @@ POST_SYSCALL(vfork)(long res) { } } // extern "C" -#endif - #undef PRE_SYSCALL #undef PRE_READ #undef PRE_WRITE diff --git a/libsanitizer/sanitizer_common/sanitizer_coverage.cc b/libsanitizer/sanitizer_common/sanitizer_coverage.cc new file mode 100644 index 0000000..e87b76c --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_coverage.cc @@ -0,0 +1,111 @@ +//===-- sanitizer_coverage.cc ---------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Sanitizer Coverage. +// This file implements run-time support for a poor man's coverage tool. +// +// Compiler instrumentation: +// For every function F the compiler injects the following code: +// if (*Guard) { +// __sanitizer_cov(&F); +// *Guard = 1; +// } +// It's fine to call __sanitizer_cov more than once for a given function. +// +// Run-time: +// - __sanitizer_cov(pc): record that we've executed a given PC. +// - __sanitizer_cov_dump: dump the coverage data to disk. +// For every module of the current process that has coverage data +// this will create a file module_name.PID.sancov. The file format is simple: +// it's just a sorted sequence of 4-byte offsets in the module. +// +// Eventually, this coverage implementation should be obsoleted by a more +// powerful general purpose Clang/LLVM coverage instrumentation. +// Consider this implementation as prototype. +// +// FIXME: support (or at least test with) dlclose. +//===----------------------------------------------------------------------===// + +#include "sanitizer_allocator_internal.h" +#include "sanitizer_common.h" +#include "sanitizer_libc.h" +#include "sanitizer_mutex.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_flags.h" + +struct CovData { + BlockingMutex mu; + InternalMmapVector v; +}; + +static uptr cov_data_placeholder[sizeof(CovData) / sizeof(uptr)]; +COMPILER_CHECK(sizeof(cov_data_placeholder) >= sizeof(CovData)); +static CovData *cov_data = reinterpret_cast(cov_data_placeholder); + +namespace __sanitizer { + +// Simply add the pc into the vector under lock. If the function is called more +// than once for a given PC it will be inserted multiple times, which is fine. +static void CovAdd(uptr pc) { + BlockingMutexLock lock(&cov_data->mu); + cov_data->v.push_back(pc); +} + +static inline bool CompareLess(const uptr &a, const uptr &b) { + return a < b; +} + +// Dump the coverage on disk. +void CovDump() { +#if !SANITIZER_WINDOWS + BlockingMutexLock lock(&cov_data->mu); + InternalMmapVector &v = cov_data->v; + InternalSort(&v, v.size(), CompareLess); + InternalMmapVector offsets(v.size()); + const uptr *vb = v.data(); + const uptr *ve = vb + v.size(); + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + uptr mb, me, off, prot; + InternalScopedBuffer module(4096); + InternalScopedBuffer path(4096 * 2); + for (int i = 0; + proc_maps.Next(&mb, &me, &off, module.data(), module.size(), &prot); + i++) { + if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) + continue; + if (vb >= ve) break; + if (mb <= *vb && *vb < me) { + offsets.clear(); + const uptr *old_vb = vb; + CHECK_LE(off, *vb); + for (; vb < ve && *vb < me; vb++) { + uptr diff = *vb - (i ? mb : 0) + off; + CHECK_LE(diff, 0xffffffffU); + offsets.push_back(static_cast(diff)); + } + char *module_name = StripModuleName(module.data()); + internal_snprintf((char *)path.data(), path.size(), "%s.%zd.sancov", + module_name, internal_getpid()); + InternalFree(module_name); + uptr fd = OpenFile(path.data(), true); + internal_write(fd, offsets.data(), offsets.size() * sizeof(u32)); + internal_close(fd); + if (common_flags()->verbosity) + Report(" CovDump: %s: %zd PCs written\n", path.data(), vb - old_vb); + } + } +#endif // !SANITIZER_WINDOWS +} + +} // namespace __sanitizer + +extern "C" { +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc) { + CovAdd(reinterpret_cast(pc)); +} +SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump() { CovDump(); } +} // extern "C" diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.cc b/libsanitizer/sanitizer_common/sanitizer_flags.cc index 06ac543..90bb57d 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.cc +++ b/libsanitizer/sanitizer_common/sanitizer_flags.cc @@ -16,20 +16,40 @@ namespace __sanitizer { -CommonFlags common_flags_dont_use_directly; +void SetCommonFlagsDefaults(CommonFlags *f) { + f->symbolize = true; + f->external_symbolizer_path = 0; + f->strip_path_prefix = ""; + f->fast_unwind_on_fatal = false; + f->fast_unwind_on_malloc = true; + f->handle_ioctl = false; + f->malloc_context_size = 1; + f->log_path = "stderr"; + f->verbosity = 0; + f->detect_leaks = false; + f->leak_check_at_exit = true; + f->allocator_may_return_null = false; + f->print_summary = true; +} -void ParseCommonFlagsFromString(const char *str) { - CommonFlags *f = common_flags(); - ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); +void ParseCommonFlagsFromString(CommonFlags *f, const char *str) { + ParseFlag(str, &f->symbolize, "symbolize"); + ParseFlag(str, &f->external_symbolizer_path, "external_symbolizer_path"); ParseFlag(str, &f->strip_path_prefix, "strip_path_prefix"); ParseFlag(str, &f->fast_unwind_on_fatal, "fast_unwind_on_fatal"); ParseFlag(str, &f->fast_unwind_on_malloc, "fast_unwind_on_malloc"); - ParseFlag(str, &f->symbolize, "symbolize"); ParseFlag(str, &f->handle_ioctl, "handle_ioctl"); + ParseFlag(str, &f->malloc_context_size, "malloc_context_size"); ParseFlag(str, &f->log_path, "log_path"); + ParseFlag(str, &f->verbosity, "verbosity"); ParseFlag(str, &f->detect_leaks, "detect_leaks"); ParseFlag(str, &f->leak_check_at_exit, "leak_check_at_exit"); ParseFlag(str, &f->allocator_may_return_null, "allocator_may_return_null"); + ParseFlag(str, &f->print_summary, "print_summary"); + + // Do a sanity check for certain flags. + if (f->malloc_context_size < 1) + f->malloc_context_size = 1; } static bool GetFlagValue(const char *env, const char *name, diff --git a/libsanitizer/sanitizer_common/sanitizer_flags.h b/libsanitizer/sanitizer_common/sanitizer_flags.h index 62aa96b..46ec0928 100644 --- a/libsanitizer/sanitizer_common/sanitizer_flags.h +++ b/libsanitizer/sanitizer_common/sanitizer_flags.h @@ -23,7 +23,8 @@ void ParseFlag(const char *env, const char **flag, const char *name); struct CommonFlags { // If set, use the online symbolizer from common sanitizer runtime. bool symbolize; - // Path to external symbolizer. + // Path to external symbolizer. If it is NULL, symbolizer will be looked for + // in PATH. If it is empty, external symbolizer will not be started. const char *external_symbolizer_path; // Strips this prefix from file paths in error reports. const char *strip_path_prefix; @@ -35,8 +36,12 @@ struct CommonFlags { bool handle_ioctl; // Max number of stack frames kept for each allocation/deallocation. int malloc_context_size; - // Write logs to "log_path.pid" instead of stderr. + // Write logs to "log_path.pid". + // The special values are "stdout" and "stderr". + // The default is "stderr". const char *log_path; + // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). + int verbosity; // Enable memory leak detection. bool detect_leaks; // Invoke leak checking in an atexit handler. Has no effect if @@ -45,15 +50,17 @@ struct CommonFlags { bool leak_check_at_exit; // If false, the allocator will crash instead of returning 0 on out-of-memory. bool allocator_may_return_null; + // If false, disable printing error summaries in addition to error reports. + bool print_summary; }; -extern CommonFlags common_flags_dont_use_directly; - inline CommonFlags *common_flags() { - return &common_flags_dont_use_directly; + static CommonFlags f; + return &f; } -void ParseCommonFlagsFromString(const char *str); +void SetCommonFlagsDefaults(CommonFlags *f); +void ParseCommonFlagsFromString(CommonFlags *f, const char *str); } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h index cc9233c..0dab7c2 100644 --- a/libsanitizer/sanitizer_common/sanitizer_internal_defs.h +++ b/libsanitizer/sanitizer_common/sanitizer_internal_defs.h @@ -32,6 +32,12 @@ # define SANITIZER_SUPPORTS_WEAK_HOOKS 0 #endif +#if __LP64__ || defined(_WIN64) +# define SANITIZER_WORDSIZE 64 +#else +# define SANITIZER_WORDSIZE 32 +#endif + // GCC does not understand __has_feature #if !defined(__has_feature) # define __has_feature(x) 0 @@ -77,18 +83,20 @@ typedef u64 OFF_T; typedef uptr OFF_T; #endif typedef u64 OFF64_T; + +#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC +typedef uptr operator_new_size_type; +#else +typedef u32 operator_new_size_type; +#endif } // namespace __sanitizer extern "C" { // Tell the tools to write their reports to "path." instead of stderr. + // The special values are "stdout" and "stderr". SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_set_report_path(const char *path); - // Tell the tools to write their reports to given file descriptor instead of - // stderr. - SANITIZER_INTERFACE_ATTRIBUTE - void __sanitizer_set_report_fd(int fd); - // Notify the tools that the sandbox is going to be turned on. The reserved // parameter will be used in the future to hold a structure with functions // that the tools may call to bypass the sandbox. @@ -100,6 +108,14 @@ extern "C" { // the error message. This function can be overridden by the client. SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE void __sanitizer_report_error_summary(const char *error_summary); + + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov_dump(); + SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_cov(void *pc); + SANITIZER_INTERFACE_ATTRIBUTE + void __sanitizer_annotate_contiguous_container(const void *beg, + const void *end, + const void *old_mid, + const void *new_mid); } // extern "C" @@ -169,12 +185,6 @@ typedef void* thread_return_t; #endif // _WIN32 typedef thread_return_t (THREAD_CALLING_CONV *thread_callback_t)(void* arg); -#if __LP64__ || defined(_WIN64) -# define SANITIZER_WORDSIZE 64 -#else -# define SANITIZER_WORDSIZE 32 -#endif - // NOTE: Functions below must be defined in each run-time. namespace __sanitizer { void NORETURN Die(); diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.cc b/libsanitizer/sanitizer_common/sanitizer_libc.cc index 2a75e43..53c8755 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.cc +++ b/libsanitizer/sanitizer_common/sanitizer_libc.cc @@ -14,6 +14,16 @@ namespace __sanitizer { +// Make the compiler think that something is going on there. +static inline void break_optimization(void *arg) { +#if SANITIZER_WINDOWS + // FIXME: make sure this is actually enough. + __asm; +#else + __asm__ __volatile__("" : : "r" (arg) : "memory"); +#endif +} + s64 internal_atoll(const char *nptr) { return internal_simple_strtoll(nptr, (char**)0, 10); } @@ -60,6 +70,16 @@ void *internal_memmove(void *dest, const void *src, uptr n) { return dest; } +// Semi-fast bzero for 16-aligned data. Still far from peak performance. +void internal_bzero_aligned16(void *s, uptr n) { + struct S16 { u64 a, b; } ALIGNED(16); + CHECK_EQ((reinterpret_cast(s) | n) & 15, 0); + for (S16 *p = reinterpret_cast(s), *end = p + n / 16; p < end; p++) { + p->a = p->b = 0; + break_optimization(0); // Make sure this does not become memset. + } +} + void *internal_memset(void* s, int c, uptr n) { // The next line prevents Clang from making a call to memset() instead of the // loop below. diff --git a/libsanitizer/sanitizer_common/sanitizer_libc.h b/libsanitizer/sanitizer_common/sanitizer_libc.h index f90ffcc..ae23bc4 100644 --- a/libsanitizer/sanitizer_common/sanitizer_libc.h +++ b/libsanitizer/sanitizer_common/sanitizer_libc.h @@ -27,6 +27,8 @@ void *internal_memchr(const void *s, int c, uptr n); int internal_memcmp(const void* s1, const void* s2, uptr n); void *internal_memcpy(void *dest, const void *src, uptr n); void *internal_memmove(void *dest, const void *src, uptr n); +// Set [s, s + n) to 0. Both s and n should be 16-aligned. +void internal_bzero_aligned16(void *s, uptr n); // Should not be used in performance-critical places. void *internal_memset(void *s, int c, uptr n); char* internal_strchr(const char *s, int c); diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.cc b/libsanitizer/sanitizer_common/sanitizer_libignore.cc new file mode 100644 index 0000000..310e811 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_libignore.cc @@ -0,0 +1,103 @@ +//===-- sanitizer_libignore.cc --------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#if SANITIZER_LINUX + +#include "sanitizer_libignore.h" +#include "sanitizer_flags.h" +#include "sanitizer_procmaps.h" + +namespace __sanitizer { + +LibIgnore::LibIgnore(LinkerInitialized) { +} + +void LibIgnore::Init(const SuppressionContext &supp) { + BlockingMutexLock lock(&mutex_); + CHECK_EQ(count_, 0); + const uptr n = supp.SuppressionCount(); + for (uptr i = 0; i < n; i++) { + const Suppression *s = supp.SuppressionAt(i); + if (s->type != SuppressionLib) + continue; + if (count_ >= kMaxLibs) { + Report("%s: too many called_from_lib suppressions (max: %d)\n", + SanitizerToolName, kMaxLibs); + Die(); + } + Lib *lib = &libs_[count_++]; + lib->templ = internal_strdup(s->templ); + lib->name = 0; + lib->loaded = false; + } +} + +void LibIgnore::OnLibraryLoaded(const char *name) { + BlockingMutexLock lock(&mutex_); + // Try to match suppressions with symlink target. + InternalScopedBuffer buf(4096); + if (name != 0 && internal_readlink(name, buf.data(), buf.size() - 1) > 0 && + buf.data()[0]) { + for (uptr i = 0; i < count_; i++) { + Lib *lib = &libs_[i]; + if (!lib->loaded && lib->real_name == 0 && + TemplateMatch(lib->templ, name)) + lib->real_name = internal_strdup(buf.data()); + } + } + + // Scan suppressions list and find newly loaded and unloaded libraries. + MemoryMappingLayout proc_maps(/*cache_enabled*/false); + InternalScopedBuffer module(4096); + for (uptr i = 0; i < count_; i++) { + Lib *lib = &libs_[i]; + bool loaded = false; + proc_maps.Reset(); + uptr b, e, off, prot; + while (proc_maps.Next(&b, &e, &off, module.data(), module.size(), &prot)) { + if ((prot & MemoryMappingLayout::kProtectionExecute) == 0) + continue; + if (TemplateMatch(lib->templ, module.data()) || + (lib->real_name != 0 && + internal_strcmp(lib->real_name, module.data()) == 0)) { + if (loaded) { + Report("%s: called_from_lib suppression '%s' is matched against" + " 2 libraries: '%s' and '%s'\n", + SanitizerToolName, lib->templ, lib->name, module.data()); + Die(); + } + loaded = true; + if (lib->loaded) + continue; + if (common_flags()->verbosity) + Report("Matched called_from_lib suppression '%s' against library" + " '%s'\n", lib->templ, module.data()); + lib->loaded = true; + lib->name = internal_strdup(module.data()); + const uptr idx = atomic_load(&loaded_count_, memory_order_relaxed); + code_ranges_[idx].begin = b; + code_ranges_[idx].end = e; + atomic_store(&loaded_count_, idx + 1, memory_order_release); + } + } + if (lib->loaded && !loaded) { + Report("%s: library '%s' that was matched against called_from_lib" + " suppression '%s' is unloaded\n", + SanitizerToolName, lib->name, lib->templ); + Die(); + } + } +} + +void LibIgnore::OnLibraryUnloaded() { + OnLibraryLoaded(0); +} + +} // namespace __sanitizer + +#endif // #if SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_libignore.h b/libsanitizer/sanitizer_common/sanitizer_libignore.h new file mode 100644 index 0000000..2089c4b --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_libignore.h @@ -0,0 +1,82 @@ +//===-- sanitizer_libignore.h -----------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// LibIgnore allows to ignore all interceptors called from a particular set +// of dynamic libraries. LibIgnore remembers all "called_from_lib" suppressions +// from the provided SuppressionContext; finds code ranges for the libraries; +// and checks whether the provided PC value belongs to the code ranges. +// +//===----------------------------------------------------------------------===// + +#ifndef SANITIZER_LIBIGNORE_H +#define SANITIZER_LIBIGNORE_H + +#include "sanitizer_internal_defs.h" +#include "sanitizer_common.h" +#include "sanitizer_suppressions.h" +#include "sanitizer_atomic.h" +#include "sanitizer_mutex.h" + +namespace __sanitizer { + +class LibIgnore { + public: + explicit LibIgnore(LinkerInitialized); + + // Fetches all "called_from_lib" suppressions from the SuppressionContext. + void Init(const SuppressionContext &supp); + + // Must be called after a new dynamic library is loaded. + void OnLibraryLoaded(const char *name); + + // Must be called after a dynamic library is unloaded. + void OnLibraryUnloaded(); + + // Checks whether the provided PC belongs to one of the ignored libraries. + bool IsIgnored(uptr pc) const; + + private: + struct Lib { + char *templ; + char *name; + char *real_name; // target of symlink + bool loaded; + }; + + struct LibCodeRange { + uptr begin; + uptr end; + }; + + static const uptr kMaxLibs = 128; + + // Hot part: + atomic_uintptr_t loaded_count_; + LibCodeRange code_ranges_[kMaxLibs]; + + // Cold part: + BlockingMutex mutex_; + uptr count_; + Lib libs_[kMaxLibs]; + + // Disallow copying of LibIgnore objects. + LibIgnore(const LibIgnore&); // not implemented + void operator = (const LibIgnore&); // not implemented +}; + +inline bool LibIgnore::IsIgnored(uptr pc) const { + const uptr n = atomic_load(&loaded_count_, memory_order_acquire); + for (uptr i = 0; i < n; i++) { + if (pc >= code_ranges_[i].begin && pc < code_ranges_[i].end) + return true; + } + return false; +} + +} // namespace __sanitizer + +#endif // SANITIZER_LIBIGNORE_H diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.cc b/libsanitizer/sanitizer_common/sanitizer_linux.cc index ddc6546..69c9c10 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.cc +++ b/libsanitizer/sanitizer_common/sanitizer_linux.cc @@ -77,7 +77,8 @@ namespace __sanitizer { uptr internal_mmap(void *addr, uptr length, int prot, int flags, int fd, u64 offset) { #if SANITIZER_LINUX_USES_64BIT_SYSCALLS - return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd, offset); + return internal_syscall(__NR_mmap, (uptr)addr, length, prot, flags, fd, + offset); #else return internal_syscall(__NR_mmap2, addr, length, prot, flags, fd, offset); #endif @@ -216,7 +217,8 @@ uptr GetTid() { } u64 NanoTime() { - kernel_timeval tv = {}; + kernel_timeval tv; + internal_memset(&tv, 0, sizeof(tv)); internal_syscall(__NR_gettimeofday, (uptr)&tv, 0); return (u64)tv.tv_sec * 1000*1000*1000 + tv.tv_usec * 1000; } @@ -309,7 +311,8 @@ void PrepareForSandboxing() { MemoryMappingLayout::CacheMemoryMappings(); // Same for /proc/self/exe in the symbolizer. #if !SANITIZER_GO - getSymbolizer()->PrepareForSandboxing(); + if (Symbolizer *sym = Symbolizer::GetOrNull()) + sym->PrepareForSandboxing(); #endif } @@ -572,7 +575,8 @@ uptr internal_ptrace(int request, int pid, void *addr, void *data) { } uptr internal_waitpid(int pid, int *status, int options) { - return internal_syscall(__NR_wait4, pid, (uptr)status, options, 0 /* rusage */); + return internal_syscall(__NR_wait4, pid, (uptr)status, options, + 0 /* rusage */); } uptr internal_getpid() { @@ -600,6 +604,31 @@ uptr internal_sigaltstack(const struct sigaltstack *ss, return internal_syscall(__NR_sigaltstack, (uptr)ss, (uptr)oss); } +uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act, + __sanitizer_kernel_sigaction_t *oldact) { + return internal_syscall(__NR_rt_sigaction, signum, act, oldact, + sizeof(__sanitizer_kernel_sigset_t)); +} + +uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set, + __sanitizer_kernel_sigset_t *oldset) { + return internal_syscall(__NR_rt_sigprocmask, (uptr)how, &set->sig[0], + &oldset->sig[0], sizeof(__sanitizer_kernel_sigset_t)); +} + +void internal_sigfillset(__sanitizer_kernel_sigset_t *set) { + internal_memset(set, 0xff, sizeof(*set)); +} + +void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum) { + signum -= 1; + CHECK_GE(signum, 0); + CHECK_LT(signum, sizeof(*set) * 8); + const uptr idx = signum / (sizeof(set->sig[0]) * 8); + const uptr bit = signum % (sizeof(set->sig[0]) * 8); + set->sig[idx] &= ~(1 << bit); +} + // ThreadLister implementation. ThreadLister::ThreadLister(int pid) : pid_(pid), @@ -775,8 +804,8 @@ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, child_stack = (char *)child_stack - 2 * sizeof(unsigned long long); ((unsigned long long *)child_stack)[0] = (uptr)fn; ((unsigned long long *)child_stack)[1] = (uptr)arg; - register void *r8 __asm__ ("r8") = newtls; - register int *r10 __asm__ ("r10") = child_tidptr; + register void *r8 __asm__("r8") = newtls; + register int *r10 __asm__("r10") = child_tidptr; __asm__ __volatile__( /* %rax = syscall(%rax = __NR_clone, * %rdi = flags, diff --git a/libsanitizer/sanitizer_common/sanitizer_linux.h b/libsanitizer/sanitizer_common/sanitizer_linux.h index 5bbf479..6422df1 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux.h +++ b/libsanitizer/sanitizer_common/sanitizer_linux.h @@ -15,6 +15,7 @@ #if SANITIZER_LINUX #include "sanitizer_common.h" #include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" struct link_map; // Opaque type returned by dlopen(). struct sigaltstack; @@ -29,6 +30,13 @@ uptr internal_getdents(fd_t fd, struct linux_dirent *dirp, unsigned int count); uptr internal_prctl(int option, uptr arg2, uptr arg3, uptr arg4, uptr arg5); uptr internal_sigaltstack(const struct sigaltstack* ss, struct sigaltstack* oss); +uptr internal_sigaction(int signum, const __sanitizer_kernel_sigaction_t *act, + __sanitizer_kernel_sigaction_t *oldact); +uptr internal_sigprocmask(int how, __sanitizer_kernel_sigset_t *set, + __sanitizer_kernel_sigset_t *oldset); +void internal_sigfillset(__sanitizer_kernel_sigset_t *set); +void internal_sigdelset(__sanitizer_kernel_sigset_t *set, int signum); + #ifdef __x86_64__ uptr internal_clone(int (*fn)(void *), void *child_stack, int flags, void *arg, int *parent_tidptr, void *newtls, int *child_tidptr); @@ -56,7 +64,7 @@ class ThreadLister { int bytes_read_; }; -void AdjustStackSizeLinux(void *attr, int verbosity); +void AdjustStackSizeLinux(void *attr); // Exposed for testing. uptr ThreadDescriptorSize(); @@ -74,7 +82,6 @@ void CacheBinaryName(); // Call cb for each region mapped by map. void ForEachMappedRegion(link_map *map, void (*cb)(const void *, uptr)); - } // namespace __sanitizer #endif // SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc index 7d6c639..5b70a69 100644 --- a/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_linux_libcdep.cc @@ -14,6 +14,7 @@ #if SANITIZER_LINUX #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" @@ -30,6 +31,12 @@ #include #endif +// This function is defined elsewhere if we intercepted pthread_attr_getstack. +SANITIZER_WEAK_ATTRIBUTE +int __sanitizer_pthread_attr_getstack(void *attr, void **addr, size_t *size) { + return pthread_attr_getstack((pthread_attr_t*)attr, addr, size); +} + namespace __sanitizer { void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, @@ -71,7 +78,7 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, CHECK_EQ(pthread_getattr_np(pthread_self(), &attr), 0); uptr stacksize = 0; void *stackaddr = 0; - pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); + __sanitizer_pthread_attr_getstack(&attr, &stackaddr, (size_t*)&stacksize); pthread_attr_destroy(&attr); CHECK_LE(stacksize, kMaxThreadStackSize); // Sanity check. @@ -137,35 +144,33 @@ uptr Unwind_GetIP(struct _Unwind_Context *ctx) { #endif } +struct UnwindTraceArg { + StackTrace *stack; + uptr max_depth; +}; + _Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { - StackTrace *b = (StackTrace*)param; - CHECK(b->size < b->max_size); + UnwindTraceArg *arg = (UnwindTraceArg*)param; + CHECK_LT(arg->stack->size, arg->max_depth); uptr pc = Unwind_GetIP(ctx); - b->trace[b->size++] = pc; - if (b->size == b->max_size) return UNWIND_STOP; + arg->stack->trace[arg->stack->size++] = pc; + if (arg->stack->size == arg->max_depth) return UNWIND_STOP; return UNWIND_CONTINUE; } -static bool MatchPc(uptr cur_pc, uptr trace_pc) { - return cur_pc - trace_pc <= 64 || trace_pc - cur_pc <= 64; -} - void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { - this->size = 0; - this->max_size = max_depth; - if (max_depth > 1) { - _Unwind_Backtrace(Unwind_Trace, this); - // We need to pop a few frames so that pc is on top. - // trace[0] belongs to the current function so we always pop it. - int to_pop = 1; - /**/ if (size > 1 && MatchPc(pc, trace[1])) to_pop = 1; - else if (size > 2 && MatchPc(pc, trace[2])) to_pop = 2; - else if (size > 3 && MatchPc(pc, trace[3])) to_pop = 3; - else if (size > 4 && MatchPc(pc, trace[4])) to_pop = 4; - else if (size > 5 && MatchPc(pc, trace[5])) to_pop = 5; - this->PopStackFrames(to_pop); - } - this->trace[0] = pc; + size = 0; + if (max_depth == 0) + return; + 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) + to_pop = 1; + PopStackFrames(to_pop); + trace[0] = pc; } #endif // !SANITIZER_GO @@ -265,11 +270,11 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, #endif // SANITIZER_GO } -void AdjustStackSizeLinux(void *attr_, int verbosity) { +void AdjustStackSizeLinux(void *attr_) { pthread_attr_t *attr = (pthread_attr_t *)attr_; uptr stackaddr = 0; size_t stacksize = 0; - pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); + __sanitizer_pthread_attr_getstack(attr, (void**)&stackaddr, &stacksize); // GLibC will return (0 - stacksize) as the stack address in the case when // stacksize is set, but stackaddr is not. bool stack_set = (stackaddr != 0) && (stackaddr + stacksize != 0); @@ -277,7 +282,7 @@ void AdjustStackSizeLinux(void *attr_, int verbosity) { const uptr minstacksize = GetTlsSize() + 128*1024; if (stacksize < minstacksize) { if (!stack_set) { - if (verbosity && stacksize != 0) + if (common_flags()->verbosity && stacksize != 0) Printf("Sanitizer: increasing stacksize %zu->%zu\n", stacksize, minstacksize); pthread_attr_setstacksize(attr, minstacksize); diff --git a/libsanitizer/sanitizer_common/sanitizer_mac.cc b/libsanitizer/sanitizer_common/sanitizer_mac.cc index fa146b5..288e31c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mac.cc +++ b/libsanitizer/sanitizer_common/sanitizer_mac.cc @@ -143,7 +143,11 @@ void GetThreadStackTopAndBottom(bool at_initialization, uptr *stack_top, const char *GetEnv(const char *name) { char ***env_ptr = _NSGetEnviron(); - CHECK(env_ptr); + if (!env_ptr) { + Report("_NSGetEnviron() returned NULL. Please make sure __asan_init() is " + "called after libSystem_initializer().\n"); + CHECK(env_ptr); + } char **environ = *env_ptr; CHECK(environ); uptr name_len = internal_strlen(name); diff --git a/libsanitizer/sanitizer_common/sanitizer_mutex.h b/libsanitizer/sanitizer_common/sanitizer_mutex.h index b350114..d78f43e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_mutex.h +++ b/libsanitizer/sanitizer_common/sanitizer_mutex.h @@ -38,6 +38,10 @@ class StaticSpinMutex { atomic_store(&state_, 0, memory_order_release); } + void CheckLocked() { + CHECK_EQ(atomic_load(&state_, memory_order_relaxed), 1); + } + private: atomic_uint8_t state_; diff --git a/libsanitizer/sanitizer_common/sanitizer_placement_new.h b/libsanitizer/sanitizer_common/sanitizer_placement_new.h index 310327f..7231e96 100644 --- a/libsanitizer/sanitizer_common/sanitizer_placement_new.h +++ b/libsanitizer/sanitizer_common/sanitizer_placement_new.h @@ -16,15 +16,7 @@ #include "sanitizer_internal_defs.h" -namespace __sanitizer { -#if (SANITIZER_WORDSIZE == 64) || SANITIZER_MAC -typedef uptr operator_new_ptr_type; -#else -typedef u32 operator_new_ptr_type; -#endif -} // namespace __sanitizer - -inline void *operator new(__sanitizer::operator_new_ptr_type sz, void *p) { +inline void *operator new(__sanitizer::operator_new_size_type sz, void *p) { return p; } diff --git a/libsanitizer/sanitizer_common/sanitizer_platform.h b/libsanitizer/sanitizer_common/sanitizer_platform.h index 2270709..7693fe7 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform.h @@ -23,8 +23,15 @@ #if defined(__APPLE__) # define SANITIZER_MAC 1 +# include +# if TARGET_OS_IPHONE +# define SANITIZER_IOS 1 +# else +# define SANITIZER_IOS 0 +# endif #else # define SANITIZER_MAC 0 +# define SANITIZER_IOS 0 #endif #if defined(_WIN32) diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h index e019948..b6dcbe9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_interceptors.h @@ -39,6 +39,12 @@ # define SI_MAC 0 #endif +#if SANITIZER_IOS +# define SI_IOS 1 +#else +# define SI_IOS 0 +#endif + # define SANITIZER_INTERCEPT_STRCMP 1 # define SANITIZER_INTERCEPT_STRCASECMP SI_NOT_WINDOWS @@ -61,6 +67,7 @@ # define SANITIZER_INTERCEPT_PRCTL SI_LINUX # define SANITIZER_INTERCEPT_LOCALTIME_AND_FRIENDS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STRPTIME SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_SCANF SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_ISOC99_SCANF SI_LINUX @@ -111,6 +118,7 @@ # define SANITIZER_INTERCEPT_SCHED_GETAFFINITY SI_LINUX_NOT_ANDROID # define SANITIZER_INTERCEPT_STRERROR SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_STRERROR_R SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_XPG_STRERROR_R SI_LINUX_NOT_ANDROID # define SANITIZER_INTERCEPT_SCANDIR SI_LINUX_NOT_ANDROID # define SANITIZER_INTERCEPT_SCANDIR64 SI_LINUX_NOT_ANDROID # define SANITIZER_INTERCEPT_GETGROUPS SI_NOT_WINDOWS @@ -124,5 +132,41 @@ # define SANITIZER_INTERCEPT_SIGPENDING SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_SIGPROCMASK SI_NOT_WINDOWS # define SANITIZER_INTERCEPT_BACKTRACE SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_GETMNTENT SI_LINUX +# define SANITIZER_INTERCEPT_GETMNTENT_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATFS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_STATFS64 \ + (SI_MAC && !SI_IOS) || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATVFS SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_STATVFS64 SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_INITGROUPS SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ETHER SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_ETHER_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_SHMCTL \ + (SI_LINUX_NOT_ANDROID && SANITIZER_WORDSIZE == 64) +# define SANITIZER_INTERCEPT_RANDOM_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GET SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETINHERITSCHED \ + SI_MAC || SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_PTHREAD_ATTR_GETAFFINITY_NP SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TMPNAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_TMPNAM_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TEMPNAM SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_SINCOS SI_LINUX +# define SANITIZER_INTERCEPT_REMQUO SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_LGAMMA SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_LGAMMA_R SI_LINUX +# define SANITIZER_INTERCEPT_DRAND48_R SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_ICONV SI_LINUX_NOT_ANDROID +# define SANITIZER_INTERCEPT_TIMES SI_NOT_WINDOWS + +// FIXME: getline seems to be available on OSX 10.7 +# define SANITIZER_INTERCEPT_GETLINE SI_LINUX_NOT_ANDROID + +# define SANITIZER_INTERCEPT__EXIT SI_LINUX + +# define SANITIZER_INTERCEPT_PHTREAD_MUTEX SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_COND SI_NOT_WINDOWS +# define SANITIZER_INTERCEPT_PTHREAD_SETNAME_NP SI_LINUX_NOT_ANDROID #endif // #ifndef SANITIZER_PLATFORM_INTERCEPTORS_H diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc index a69b11f..01de9c9 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_linux.cc @@ -14,34 +14,88 @@ // userspace headers. // Most "normal" includes go in sanitizer_platform_limits_posix.cc -#ifdef SYSCALL_INTERCEPTION #include "sanitizer_platform.h" #if SANITIZER_LINUX +#include "sanitizer_internal_defs.h" +#include "sanitizer_platform_limits_posix.h" + +// For offsetof -> __builtin_offsetof definition. +#include + +// With old kernels (and even new kernels on powerpc) asm/stat.h uses types that +// are not defined anywhere in userspace headers. Fake them. This seems to work +// fine with newer headers, too. +#include +#define ino_t __kernel_ino_t +#define mode_t __kernel_mode_t +#define nlink_t __kernel_nlink_t +#define uid_t __kernel_uid_t +#define gid_t __kernel_gid_t +#define off_t __kernel_off_t // This header seems to contain the definitions of _kernel_ stat* structs. #include +#undef ino_t +#undef mode_t +#undef nlink_t +#undef uid_t +#undef gid_t +#undef off_t + #include +#if SANITIZER_ANDROID +#include +#else +#include +#endif + #if !SANITIZER_ANDROID #include #endif namespace __sanitizer { - unsigned struct___old_kernel_stat_sz = sizeof(struct __old_kernel_stat); - unsigned struct_kernel_stat_sz = sizeof(struct stat); - unsigned struct_io_event_sz = sizeof(struct io_event); - unsigned struct_iocb_sz = sizeof(struct iocb); + unsigned struct_statfs64_sz = sizeof(struct statfs64); +} // namespace __sanitizer -#if !defined(_LP64) && !defined(__x86_64__) - unsigned struct_kernel_stat64_sz = sizeof(struct stat64); -#else - unsigned struct_kernel_stat64_sz = 0; +#if !defined(__powerpc64__) +COMPILER_CHECK(struct___old_kernel_stat_sz == sizeof(struct __old_kernel_stat)); +#endif + +COMPILER_CHECK(struct_kernel_stat_sz == sizeof(struct stat)); + +#if defined(__i386__) +COMPILER_CHECK(struct_kernel_stat64_sz == sizeof(struct stat64)); #endif +CHECK_TYPE_SIZE(io_event); +CHECK_SIZE_AND_OFFSET(io_event, data); +CHECK_SIZE_AND_OFFSET(io_event, obj); +CHECK_SIZE_AND_OFFSET(io_event, res); +CHECK_SIZE_AND_OFFSET(io_event, res2); + #if !SANITIZER_ANDROID - unsigned struct_perf_event_attr_sz = sizeof(struct perf_event_attr); +COMPILER_CHECK(sizeof(struct __sanitizer_perf_event_attr) <= + sizeof(struct perf_event_attr)); +CHECK_SIZE_AND_OFFSET(perf_event_attr, type); +CHECK_SIZE_AND_OFFSET(perf_event_attr, size); #endif -} // namespace __sanitizer -#endif // SANITIZER_LINUX +COMPILER_CHECK(iocb_cmd_pread == IOCB_CMD_PREAD); +COMPILER_CHECK(iocb_cmd_pwrite == IOCB_CMD_PWRITE); +#if !SANITIZER_ANDROID +COMPILER_CHECK(iocb_cmd_preadv == IOCB_CMD_PREADV); +COMPILER_CHECK(iocb_cmd_pwritev == IOCB_CMD_PWRITEV); #endif + +CHECK_TYPE_SIZE(iocb); +CHECK_SIZE_AND_OFFSET(iocb, aio_data); +// Skip aio_key, it's weird. +CHECK_SIZE_AND_OFFSET(iocb, aio_lio_opcode); +CHECK_SIZE_AND_OFFSET(iocb, aio_reqprio); +CHECK_SIZE_AND_OFFSET(iocb, aio_fildes); +CHECK_SIZE_AND_OFFSET(iocb, aio_buf); +CHECK_SIZE_AND_OFFSET(iocb, aio_nbytes); +CHECK_SIZE_AND_OFFSET(iocb, aio_offset); + +#endif // SANITIZER_LINUX diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc index 950ab58..058f40a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.cc @@ -19,6 +19,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,8 @@ #include #if SANITIZER_LINUX +#include +#include #include #include #include @@ -75,6 +78,7 @@ #include #include #include +#include #include #include #include @@ -87,6 +91,8 @@ #include #include #include +#include +#include #endif // SANITIZER_LINUX && !SANITIZER_ANDROID #if SANITIZER_ANDROID @@ -100,20 +106,22 @@ #include #include #include -// #include #include #endif // SANITIZER_LINUX #if SANITIZER_MAC -#include +#include #include +#include #include #endif namespace __sanitizer { unsigned struct_utsname_sz = sizeof(struct utsname); unsigned struct_stat_sz = sizeof(struct stat); +#if !SANITIZER_IOS unsigned struct_stat64_sz = sizeof(struct stat64); +#endif // !SANITIZER_IOS unsigned struct_rusage_sz = sizeof(struct rusage); unsigned struct_tm_sz = sizeof(struct tm); unsigned struct_passwd_sz = sizeof(struct passwd); @@ -122,6 +130,7 @@ namespace __sanitizer { unsigned struct_sigaction_sz = sizeof(struct sigaction); unsigned struct_itimerval_sz = sizeof(struct itimerval); unsigned pthread_t_sz = sizeof(pthread_t); + unsigned pthread_cond_t_sz = sizeof(pthread_cond_t); unsigned pid_t_sz = sizeof(pid_t); unsigned timeval_sz = sizeof(timeval); unsigned uid_t_sz = sizeof(uid_t); @@ -131,6 +140,11 @@ namespace __sanitizer { unsigned struct_tms_sz = sizeof(struct tms); unsigned struct_sigevent_sz = sizeof(struct sigevent); unsigned struct_sched_param_sz = sizeof(struct sched_param); + unsigned struct_statfs_sz = sizeof(struct statfs); + +#if SANITIZER_MAC && !SANITIZER_IOS + unsigned struct_statfs64_sz = sizeof(struct statfs64); +#endif // SANITIZER_MAC && !SANITIZER_IOS #if !SANITIZER_ANDROID unsigned ucontext_t_sz = sizeof(ucontext_t); @@ -138,7 +152,6 @@ namespace __sanitizer { #if SANITIZER_LINUX unsigned struct_rlimit_sz = sizeof(struct rlimit); - unsigned struct_statfs_sz = sizeof(struct statfs); unsigned struct_epoll_event_sz = sizeof(struct epoll_event); unsigned struct_sysinfo_sz = sizeof(struct sysinfo); unsigned struct_timespec_sz = sizeof(struct timespec); @@ -155,11 +168,11 @@ namespace __sanitizer { #if SANITIZER_LINUX && !SANITIZER_ANDROID unsigned struct_rlimit64_sz = sizeof(struct rlimit64); - unsigned struct_statfs64_sz = sizeof(struct statfs64); unsigned struct_timex_sz = sizeof(struct timex); unsigned struct_msqid_ds_sz = sizeof(struct msqid_ds); - unsigned struct_shmid_ds_sz = sizeof(struct shmid_ds); unsigned struct_mq_attr_sz = sizeof(struct mq_attr); + unsigned struct_statvfs_sz = sizeof(struct statvfs); + unsigned struct_statvfs64_sz = sizeof(struct statvfs64); #endif // SANITIZER_LINUX && !SANITIZER_ANDROID uptr sig_ign = (uptr)SIG_IGN; @@ -170,6 +183,16 @@ namespace __sanitizer { int e_tabsz = (int)E_TABSZ; #endif + +#if SANITIZER_LINUX && !SANITIZER_ANDROID + unsigned struct_shminfo_sz = sizeof(struct shminfo); + unsigned struct_shm_info_sz = sizeof(struct shm_info); + int shmctl_ipc_stat = (int)IPC_STAT; + int shmctl_ipc_info = (int)IPC_INFO; + int shmctl_shm_info = (int)SHM_INFO; + int shmctl_shm_stat = (int)SHM_INFO; +#endif + int af_inet = (int)AF_INET; int af_inet6 = (int)AF_INET6; @@ -197,6 +220,9 @@ namespace __sanitizer { unsigned struct_user_fpxregs_struct_sz = sizeof(struct user_fpxregs_struct); #endif + int ptrace_peektext = PTRACE_PEEKTEXT; + int ptrace_peekdata = PTRACE_PEEKDATA; + int ptrace_peekuser = PTRACE_PEEKUSER; int ptrace_getregs = PTRACE_GETREGS; int ptrace_setregs = PTRACE_SETREGS; int ptrace_getfpregs = PTRACE_GETFPREGS; @@ -295,7 +321,7 @@ namespace __sanitizer { unsigned struct_unimapinit_sz = sizeof(struct unimapinit); #endif -#if !SANITIZER_ANDROID +#if !SANITIZER_ANDROID && !SANITIZER_MAC unsigned struct_sioc_sg_req_sz = sizeof(struct sioc_sg_req); unsigned struct_sioc_vif_req_sz = sizeof(struct sioc_vif_req); #endif @@ -346,7 +372,7 @@ namespace __sanitizer { unsigned IOCTL_TIOCSPGRP = TIOCSPGRP; unsigned IOCTL_TIOCSTI = TIOCSTI; unsigned IOCTL_TIOCSWINSZ = TIOCSWINSZ; -#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_MAC +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) unsigned IOCTL_SIOCGETSGCNT = SIOCGETSGCNT; unsigned IOCTL_SIOCGETVIFCNT = SIOCGETVIFCNT; #endif @@ -733,24 +759,9 @@ namespace __sanitizer { unsigned IOCTL_TIOCSERSETMULTI = TIOCSERSETMULTI; unsigned IOCTL_TIOCSSERIAL = TIOCSSERIAL; #endif -} // namespace __sanitizer -#define CHECK_TYPE_SIZE(TYPE) \ - COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE)) - -#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \ - COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \ - sizeof(((CLASS *) NULL)->MEMBER)); \ - COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \ - offsetof(CLASS, MEMBER)) - -// For sigaction, which is a function and struct at the same time, -// and thus requires explicit "struct" in sizeof() expression. -#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \ - COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \ - sizeof(((struct CLASS *) NULL)->MEMBER)); \ - COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \ - offsetof(struct CLASS, MEMBER)) + extern const int errno_EOWNERDEAD = EOWNERDEAD; +} // namespace __sanitizer COMPILER_CHECK(sizeof(__sanitizer_pthread_attr_t) >= sizeof(pthread_attr_t)); @@ -855,7 +866,6 @@ CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_flags); CHECK_STRUCT_SIZE_AND_OFFSET(sigaction, sa_restorer); #endif -#ifdef SYSCALL_INTERCEPTION #if SANITIZER_LINUX CHECK_TYPE_SIZE(__sysctl_args); CHECK_SIZE_AND_OFFSET(__sysctl_args, name); @@ -873,7 +883,6 @@ CHECK_TYPE_SIZE(__kernel_off_t); CHECK_TYPE_SIZE(__kernel_loff_t); CHECK_TYPE_SIZE(__kernel_fd_set); #endif -#endif #if !SANITIZER_ANDROID CHECK_TYPE_SIZE(wordexp_t); @@ -882,4 +891,52 @@ CHECK_SIZE_AND_OFFSET(wordexp_t, we_wordv); CHECK_SIZE_AND_OFFSET(wordexp_t, we_offs); #endif +CHECK_TYPE_SIZE(tm); +CHECK_SIZE_AND_OFFSET(tm, tm_sec); +CHECK_SIZE_AND_OFFSET(tm, tm_min); +CHECK_SIZE_AND_OFFSET(tm, tm_hour); +CHECK_SIZE_AND_OFFSET(tm, tm_mday); +CHECK_SIZE_AND_OFFSET(tm, tm_mon); +CHECK_SIZE_AND_OFFSET(tm, tm_year); +CHECK_SIZE_AND_OFFSET(tm, tm_wday); +CHECK_SIZE_AND_OFFSET(tm, tm_yday); +CHECK_SIZE_AND_OFFSET(tm, tm_isdst); +CHECK_SIZE_AND_OFFSET(tm, tm_gmtoff); +CHECK_SIZE_AND_OFFSET(tm, tm_zone); + +#if SANITIZER_LINUX +CHECK_TYPE_SIZE(mntent); +CHECK_SIZE_AND_OFFSET(mntent, mnt_fsname); +CHECK_SIZE_AND_OFFSET(mntent, mnt_dir); +CHECK_SIZE_AND_OFFSET(mntent, mnt_type); +CHECK_SIZE_AND_OFFSET(mntent, mnt_opts); +CHECK_SIZE_AND_OFFSET(mntent, mnt_freq); +CHECK_SIZE_AND_OFFSET(mntent, mnt_passno); +#endif + +CHECK_TYPE_SIZE(ether_addr); + +#if SANITIZER_LINUX && !SANITIZER_ANDROID +CHECK_TYPE_SIZE(ipc_perm); +CHECK_SIZE_AND_OFFSET(ipc_perm, __key); +CHECK_SIZE_AND_OFFSET(ipc_perm, uid); +CHECK_SIZE_AND_OFFSET(ipc_perm, gid); +CHECK_SIZE_AND_OFFSET(ipc_perm, cuid); +CHECK_SIZE_AND_OFFSET(ipc_perm, cgid); +CHECK_SIZE_AND_OFFSET(ipc_perm, mode); +CHECK_SIZE_AND_OFFSET(ipc_perm, __seq); + +CHECK_TYPE_SIZE(shmid_ds); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_perm); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_segsz); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_atime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_dtime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_ctime); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_cpid); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_lpid); +CHECK_SIZE_AND_OFFSET(shmid_ds, shm_nattch); +#endif + +CHECK_TYPE_SIZE(clock_t); + #endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h index 67c459c..3bb8dc1 100644 --- a/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h +++ b/libsanitizer/sanitizer_common/sanitizer_platform_limits_posix.h @@ -13,19 +13,22 @@ #ifndef SANITIZER_PLATFORM_LIMITS_POSIX_H #define SANITIZER_PLATFORM_LIMITS_POSIX_H +#include "sanitizer_internal_defs.h" #include "sanitizer_platform.h" namespace __sanitizer { extern unsigned struct_utsname_sz; extern unsigned struct_stat_sz; +#if !SANITIZER_IOS extern unsigned struct_stat64_sz; +#endif extern unsigned struct_rusage_sz; - extern unsigned struct_tm_sz; extern unsigned struct_passwd_sz; extern unsigned struct_group_sz; extern unsigned siginfo_t_sz; extern unsigned struct_itimerval_sz; extern unsigned pthread_t_sz; + extern unsigned pthread_cond_t_sz; extern unsigned pid_t_sz; extern unsigned timeval_sz; extern unsigned uid_t_sz; @@ -35,30 +38,52 @@ namespace __sanitizer { extern unsigned struct_itimerspec_sz; extern unsigned struct_sigevent_sz; extern unsigned struct_sched_param_sz; + extern unsigned struct_statfs_sz; + extern unsigned struct_statfs64_sz; #if !SANITIZER_ANDROID extern unsigned ucontext_t_sz; #endif // !SANITIZER_ANDROID #if SANITIZER_LINUX - extern unsigned struct___old_kernel_stat_sz; - extern unsigned struct_kernel_stat_sz; - extern unsigned struct_kernel_stat64_sz; - extern unsigned struct_io_event_sz; - extern unsigned struct_iocb_sz; + +#if defined(__x86_64__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 144; + const unsigned struct_kernel_stat64_sz = 0; +#elif defined(__i386__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 96; +#elif defined(__arm__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 64; + const unsigned struct_kernel_stat64_sz = 104; +#elif defined(__powerpc__) && !defined(__powerpc64__) + const unsigned struct___old_kernel_stat_sz = 32; + const unsigned struct_kernel_stat_sz = 72; + const unsigned struct_kernel_stat64_sz = 104; +#elif defined(__powerpc64__) + const unsigned struct___old_kernel_stat_sz = 0; + const unsigned struct_kernel_stat_sz = 144; + const unsigned struct_kernel_stat64_sz = 104; +#endif + struct __sanitizer_perf_event_attr { + unsigned type; + unsigned size; + // More fields that vary with the kernel version. + }; + extern unsigned struct_utimbuf_sz; extern unsigned struct_new_utsname_sz; extern unsigned struct_old_utsname_sz; extern unsigned struct_oldold_utsname_sz; extern unsigned struct_msqid_ds_sz; - extern unsigned struct_shmid_ds_sz; extern unsigned struct_mq_attr_sz; - extern unsigned struct_perf_event_attr_sz; extern unsigned struct_timex_sz; extern unsigned struct_ustat_sz; extern unsigned struct_rlimit_sz; - extern unsigned struct_statfs_sz; extern unsigned struct_epoll_event_sz; extern unsigned struct_sysinfo_sz; extern unsigned struct_timespec_sz; @@ -67,6 +92,32 @@ namespace __sanitizer { const unsigned old_sigset_t_sz = sizeof(unsigned long); const unsigned struct_kexec_segment_sz = 4 * sizeof(unsigned long); + struct __sanitizer_iocb { + u64 aio_data; + u32 aio_key_or_aio_reserved1; // Simply crazy. + u32 aio_reserved1_or_aio_key; // Luckily, we don't need these. + u16 aio_lio_opcode; + s16 aio_reqprio; + u32 aio_fildes; + u64 aio_buf; + u64 aio_nbytes; + s64 aio_offset; + u64 aio_reserved2; + u64 aio_reserved3; + }; + + struct __sanitizer_io_event { + u64 data; + u64 obj; + u64 res; + u64 res2; + }; + + const unsigned iocb_cmd_pread = 0; + const unsigned iocb_cmd_pwrite = 1; + const unsigned iocb_cmd_preadv = 7; + const unsigned iocb_cmd_pwritev = 8; + struct __sanitizer___sysctl_args { int *name; int nlen; @@ -80,8 +131,55 @@ namespace __sanitizer { #if SANITIZER_LINUX && !SANITIZER_ANDROID extern unsigned struct_rlimit64_sz; - extern unsigned struct_statfs64_sz; -#endif // SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_statvfs_sz; + extern unsigned struct_statvfs64_sz; + + struct __sanitizer_ipc_perm { + int __key; + int uid; + int gid; + int cuid; + int cgid; +#ifdef __powerpc64__ + unsigned mode; + unsigned __seq; +#else + unsigned short mode; + unsigned short __pad1; + unsigned short __seq; + unsigned short __pad2; +#endif + uptr __unused1; + uptr __unused2; + }; + + struct __sanitizer_shmid_ds { + __sanitizer_ipc_perm shm_perm; + #ifndef __powerpc__ + uptr shm_segsz; + #endif + uptr shm_atime; + #ifndef _LP64 + uptr __unused1; + #endif + uptr shm_dtime; + #ifndef _LP64 + uptr __unused2; + #endif + uptr shm_ctime; + #ifndef _LP64 + uptr __unused3; + #endif + #ifdef __powerpc__ + uptr shm_segsz; + #endif + int shm_cpid; + int shm_lpid; + uptr shm_nattch; + uptr __unused4; + uptr __unused5; + }; + #endif // SANITIZER_LINUX && !SANITIZER_ANDROID struct __sanitizer_iovec { void *iov_base; @@ -94,6 +192,35 @@ namespace __sanitizer { typedef unsigned __sanitizer_pthread_key_t; #endif + struct __sanitizer_ether_addr { + u8 octet[6]; + }; + + struct __sanitizer_tm { + int tm_sec; + int tm_min; + int tm_hour; + int tm_mday; + int tm_mon; + int tm_year; + int tm_wday; + int tm_yday; + int tm_isdst; + long int tm_gmtoff; + const char *tm_zone; + }; + +#if SANITIZER_LINUX + struct __sanitizer_mntent { + char *mnt_fsname; + char *mnt_dir; + char *mnt_type; + char *mnt_opts; + int mnt_freq; + int mnt_passno; + }; +#endif + #if SANITIZER_ANDROID || SANITIZER_MAC struct __sanitizer_msghdr { void *msg_name; @@ -158,6 +285,8 @@ namespace __sanitizer { }; #endif + typedef long __sanitizer_clock_t; + #if SANITIZER_LINUX #if defined(_LP64) || defined(__x86_64__) typedef unsigned __sanitizer___kernel_uid_t; @@ -168,8 +297,15 @@ namespace __sanitizer { typedef unsigned short __sanitizer___kernel_gid_t; typedef long __sanitizer___kernel_off_t; #endif + +#if defined(__powerpc64__) + typedef unsigned int __sanitizer___kernel_old_uid_t; + typedef unsigned int __sanitizer___kernel_old_gid_t; +#else typedef unsigned short __sanitizer___kernel_old_uid_t; typedef unsigned short __sanitizer___kernel_old_gid_t; +#endif + typedef long long __sanitizer___kernel_loff_t; typedef struct { unsigned long fds_bits[1024 / (8 * sizeof(long))]; @@ -207,6 +343,20 @@ namespace __sanitizer { #endif }; + struct __sanitizer_kernel_sigset_t { + u8 sig[8]; + }; + + struct __sanitizer_kernel_sigaction_t { + union { + void (*sigaction)(int signo, void *info, void *ctx); + void (*handler)(int signo); + }; + unsigned long sa_flags; + void (*sa_restorer)(void); + __sanitizer_kernel_sigset_t sa_mask; + }; + extern uptr sig_ign; extern uptr sig_dfl; extern uptr sa_siginfo; @@ -297,6 +447,9 @@ namespace __sanitizer { extern unsigned struct_user_fpregs_struct_sz; extern unsigned struct_user_fpxregs_struct_sz; + extern int ptrace_peektext; + extern int ptrace_peekdata; + extern int ptrace_peekuser; extern int ptrace_getregs; extern int ptrace_setregs; extern int ptrace_getfpregs; @@ -309,6 +462,15 @@ namespace __sanitizer { extern int ptrace_setregset; #endif +#if SANITIZER_LINUX && !SANITIZER_ANDROID + extern unsigned struct_shminfo_sz; + extern unsigned struct_shm_info_sz; + extern int shmctl_ipc_stat; + extern int shmctl_ipc_info; + extern int shmctl_shm_info; + extern int shmctl_shm_stat; +#endif + // ioctl arguments struct __sanitizer_ifconf { int ifc_len; @@ -390,7 +552,7 @@ namespace __sanitizer { extern unsigned struct_unimapinit_sz; #endif -#if !SANITIZER_ANDROID +#if !SANITIZER_ANDROID && !SANITIZER_MAC extern unsigned struct_sioc_sg_req_sz; extern unsigned struct_sioc_vif_req_sz; #endif @@ -445,7 +607,7 @@ namespace __sanitizer { extern unsigned IOCTL_TIOCSPGRP; extern unsigned IOCTL_TIOCSTI; extern unsigned IOCTL_TIOCSWINSZ; -#if (SANITIZER_LINUX && !SANITIZER_ANDROID) || SANITIZER_MAC +#if (SANITIZER_LINUX && !SANITIZER_ANDROID) extern unsigned IOCTL_SIOCGETSGCNT; extern unsigned IOCTL_SIOCGETVIFCNT; #endif @@ -807,6 +969,25 @@ namespace __sanitizer { extern unsigned IOCTL_TIOCSERSETMULTI; extern unsigned IOCTL_TIOCSSERIAL; #endif + + extern const int errno_EOWNERDEAD; } // namespace __sanitizer +#define CHECK_TYPE_SIZE(TYPE) \ + COMPILER_CHECK(sizeof(__sanitizer_##TYPE) == sizeof(TYPE)) + +#define CHECK_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((__sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(__sanitizer_##CLASS, MEMBER) == \ + offsetof(CLASS, MEMBER)) + +// For sigaction, which is a function and struct at the same time, +// and thus requires explicit "struct" in sizeof() expression. +#define CHECK_STRUCT_SIZE_AND_OFFSET(CLASS, MEMBER) \ + COMPILER_CHECK(sizeof(((struct __sanitizer_##CLASS *) NULL)->MEMBER) == \ + sizeof(((struct CLASS *) NULL)->MEMBER)); \ + COMPILER_CHECK(offsetof(struct __sanitizer_##CLASS, MEMBER) == \ + offsetof(struct CLASS, MEMBER)) + #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc index 3d4f92d..ae782ac 100644 --- a/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_posix_libcdep.cc @@ -87,28 +87,6 @@ int internal_isatty(fd_t fd) { return isatty(fd); } -#ifndef SANITIZER_GO -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom, bool fast) { -#if !SANITIZER_CAN_FAST_UNWIND - fast = false; -#endif -#if SANITIZER_MAC - // Always unwind fast on Mac. - (void)fast; -#else - if (!fast) - return stack->SlowUnwindStack(pc, max_s); -#endif // SANITIZER_MAC - stack->size = 0; - stack->trace[0] = pc; - if (max_s > 1) { - stack->max_size = max_s; - stack->FastUnwindStack(pc, bp, stack_top, stack_bottom); - } -} -#endif // SANITIZER_GO - } // namespace __sanitizer #endif diff --git a/libsanitizer/sanitizer_common/sanitizer_printf.cc b/libsanitizer/sanitizer_common/sanitizer_printf.cc index d7ce973..08951c7 100644 --- a/libsanitizer/sanitizer_common/sanitizer_printf.cc +++ b/libsanitizer/sanitizer_common/sanitizer_printf.cc @@ -193,17 +193,22 @@ void SetPrintfAndReportCallback(void (*callback)(const char *)) { PrintfAndReportCallback = callback; } -#if SANITIZER_SUPPORTS_WEAK_HOOKS // Can be overriden in frontend. +#if SANITIZER_SUPPORTS_WEAK_HOOKS SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE +void OnPrint(const char *str) { + (void)str; +} +#elif defined(SANITIZER_GO) && defined(TSAN_EXTERNAL_HOOKS) void OnPrint(const char *str); +#else +void OnPrint(const char *str) { + (void)str; +} #endif static void CallPrintfAndReportCallback(const char *str) { -#if SANITIZER_SUPPORTS_WEAK_HOOKS - if (&OnPrint != NULL) - OnPrint(str); -#endif + OnPrint(str); if (PrintfAndReportCallback) PrintfAndReportCallback(str); } @@ -287,4 +292,13 @@ int internal_snprintf(char *buffer, uptr length, const char *format, ...) { return needed_length; } +void InternalScopedString::append(const char *format, ...) { + CHECK_LT(length_, size()); + va_list args; + va_start(args, format); + VSNPrintf(data() + length_, size() - length_, format, args); + va_end(args); + length_ += internal_strlen(data() + length_); +} + } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_quarantine.h b/libsanitizer/sanitizer_common/sanitizer_quarantine.h index 7f567f9..1e8d056 100644 --- a/libsanitizer/sanitizer_common/sanitizer_quarantine.h +++ b/libsanitizer/sanitizer_common/sanitizer_quarantine.h @@ -24,13 +24,15 @@ namespace __sanitizer { template class QuarantineCache; struct QuarantineBatch { - static const uptr kSize = 1024; + static const uptr kSize = 1021; QuarantineBatch *next; uptr size; uptr count; void *batch[kSize]; }; +COMPILER_CHECK(sizeof(QuarantineBatch) <= (1 << 13)); // 8Kb. + // The callback interface is: // void Callback::Recycle(Node *ptr); // void *cb.Allocate(uptr size); @@ -121,8 +123,10 @@ class QuarantineCache { } void Enqueue(Callback cb, void *ptr, uptr size) { - if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) + if (list_.empty() || list_.back()->count == QuarantineBatch::kSize) { AllocBatch(cb); + size += sizeof(QuarantineBatch); // Count the batch in Quarantine size. + } QuarantineBatch *b = list_.back(); b->batch[b->count++] = ptr; b->size += size; @@ -145,9 +149,7 @@ class QuarantineCache { return 0; QuarantineBatch *b = list_.front(); list_.pop_front(); - // FIXME: should probably add SizeSub method? - // See https://code.google.com/p/thread-sanitizer/issues/detail?id=20 - SizeAdd(0 - b->size); + SizeSub(b->size); return b; } @@ -158,6 +160,9 @@ class QuarantineCache { void SizeAdd(uptr add) { atomic_store(&size_, Size() + add, memory_order_relaxed); } + void SizeSub(uptr sub) { + atomic_store(&size_, Size() - sub, memory_order_relaxed); + } NOINLINE QuarantineBatch* AllocBatch(Callback cb) { QuarantineBatch *b = (QuarantineBatch *)cb.Allocate(sizeof(*b)); diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc index 4e79724..3a9e902 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.cc @@ -10,21 +10,13 @@ //===----------------------------------------------------------------------===// #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_procmaps.h" #include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -const char *StripPathPrefix(const char *filepath, - const char *strip_file_prefix) { - if (filepath == 0) return 0; - const char *prefix_beg = internal_strstr(filepath, strip_file_prefix); - if (prefix_beg) - return prefix_beg + internal_strlen(strip_file_prefix); - return filepath; -} -// ----------------------- StackTrace ----------------------------- {{{1 uptr StackTrace::GetPreviousInstructionPc(uptr pc) { #ifdef __arm__ // Cancel Thumb bit. @@ -40,32 +32,21 @@ uptr StackTrace::GetPreviousInstructionPc(uptr pc) { #endif } -static void PrintStackFramePrefix(uptr frame_num, uptr pc) { - Printf(" #%zu 0x%zx", frame_num, pc); -} - -static void PrintSourceLocation(const char *file, int line, int column, - const char *strip_file_prefix) { - CHECK(file); - Printf(" %s", StripPathPrefix(file, strip_file_prefix)); - if (line > 0) { - Printf(":%d", line); - if (column > 0) - Printf(":%d", column); - } -} - -static void PrintModuleAndOffset(const char *module, uptr offset, - const char *strip_file_prefix) { - Printf(" (%s+0x%zx)", StripPathPrefix(module, strip_file_prefix), offset); +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, - bool symbolize, const char *strip_file_prefix, - SymbolizeCallback symbolize_callback ) { + SymbolizeCallback symbolize_callback) { + if (addr == 0 || size == 0) { + Printf(" \n\n"); + return; + } MemoryMappingLayout proc_maps(/*cache_enabled*/true); InternalScopedBuffer buff(GetPageSizeCached() * 2); InternalScopedBuffer addr_frames(64); + InternalScopedString frame_desc(GetPageSizeCached() * 2); uptr frame_num = 0; for (uptr i = 0; i < size && addr[i]; i++) { // PCs in stack traces are actually the return addresses, that is, @@ -76,50 +57,60 @@ void StackTrace::PrintStack(const uptr *addr, uptr size, if (symbolize_callback) { if (symbolize_callback((void*)pc, buff.data(), buff.size())) { addr_frames_num = 1; - PrintStackFramePrefix(frame_num, pc); + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); // We can't know anything about the string returned by external // symbolizer, but if it starts with filename, try to strip path prefix // from it. - Printf(" %s\n", StripPathPrefix(buff.data(), strip_file_prefix)); + frame_desc.append( + " %s", + StripPathPrefix(buff.data(), common_flags()->strip_path_prefix)); + Printf("%s\n", frame_desc.data()); frame_num++; } } - if (symbolize && addr_frames_num == 0 && &getSymbolizer) { + if (common_flags()->symbolize && addr_frames_num == 0) { // Use our own (online) symbolizer, if necessary. - addr_frames_num = getSymbolizer()->SymbolizeCode( - pc, addr_frames.data(), addr_frames.size()); + if (Symbolizer *sym = Symbolizer::GetOrNull()) + addr_frames_num = + sym->SymbolizeCode(pc, addr_frames.data(), addr_frames.size()); for (uptr j = 0; j < addr_frames_num; j++) { AddressInfo &info = addr_frames[j]; - PrintStackFramePrefix(frame_num, pc); + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); if (info.function) { - Printf(" in %s", info.function); + frame_desc.append(" in %s", info.function); } if (info.file) { - PrintSourceLocation(info.file, info.line, info.column, - strip_file_prefix); + frame_desc.append(" "); + PrintSourceLocation(&frame_desc, info.file, info.line, info.column); } else if (info.module) { - PrintModuleAndOffset(info.module, info.module_offset, - strip_file_prefix); + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, info.module, info.module_offset); } - Printf("\n"); - info.Clear(); + Printf("%s\n", frame_desc.data()); frame_num++; + info.Clear(); } } if (addr_frames_num == 0) { // If online symbolization failed, try to output at least module and // offset for instruction. - PrintStackFramePrefix(frame_num, pc); + frame_desc.clear(); + PrintStackFramePrefix(&frame_desc, frame_num, pc); uptr offset; if (proc_maps.GetObjectNameAndOffset(pc, &offset, buff.data(), buff.size(), /* protection */0)) { - PrintModuleAndOffset(buff.data(), offset, strip_file_prefix); + frame_desc.append(" "); + PrintModuleAndOffset(&frame_desc, buff.data(), offset); } - Printf("\n"); + Printf("%s\n", frame_desc.data()); frame_num++; } } + // Always print a trailing empty line after stack trace. + Printf("\n"); } uptr StackTrace::GetCurrentPc() { @@ -127,8 +118,13 @@ uptr StackTrace::GetCurrentPc() { } void StackTrace::FastUnwindStack(uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom) { - CHECK(size == 0 && trace[0] == pc); + uptr stack_top, uptr stack_bottom, + uptr max_depth) { + if (max_depth == 0) { + size = 0; + return; + } + trace[0] = pc; size = 1; uhwptr *frame = (uhwptr *)bp; uhwptr *prev_frame = frame - 1; @@ -138,7 +134,7 @@ void StackTrace::FastUnwindStack(uptr pc, uptr bp, frame < (uhwptr *)stack_top - 2 && frame > (uhwptr *)stack_bottom && IsAligned((uptr)frame, sizeof(*frame)) && - size < max_size) { + size < max_depth) { uhwptr pc1 = frame[1]; if (pc1 != pc) { trace[size++] = (uptr) pc1; @@ -156,111 +152,19 @@ void StackTrace::PopStackFrames(uptr count) { } } -// On 32-bits we don't compress stack traces. -// On 64-bits we compress stack traces: if a given pc differes slightly from -// the previous one, we record a 31-bit offset instead of the full pc. -SANITIZER_INTERFACE_ATTRIBUTE -uptr StackTrace::CompressStack(StackTrace *stack, u32 *compressed, uptr size) { -#if SANITIZER_WORDSIZE == 32 - // Don't compress, just copy. - uptr res = 0; - for (uptr i = 0; i < stack->size && i < size; i++) { - compressed[i] = stack->trace[i]; - res++; - } - if (stack->size < size) - compressed[stack->size] = 0; -#else // 64 bits, compress. - uptr prev_pc = 0; - const uptr kMaxOffset = (1ULL << 30) - 1; - uptr c_index = 0; - uptr res = 0; - for (uptr i = 0, n = stack->size; i < n; i++) { - uptr pc = stack->trace[i]; - if (!pc) break; - if ((s64)pc < 0) break; - // Printf("C pc[%zu] %zx\n", i, pc); - if (prev_pc - pc < kMaxOffset || pc - prev_pc < kMaxOffset) { - uptr offset = (s64)(pc - prev_pc); - offset |= (1U << 31); - if (c_index >= size) break; - // Printf("C co[%zu] offset %zx\n", i, offset); - compressed[c_index++] = offset; - } else { - uptr hi = pc >> 32; - uptr lo = (pc << 32) >> 32; - CHECK_EQ((hi & (1 << 31)), 0); - if (c_index + 1 >= size) break; - // Printf("C co[%zu] hi/lo: %zx %zx\n", c_index, hi, lo); - compressed[c_index++] = hi; - compressed[c_index++] = lo; - } - res++; - prev_pc = pc; - } - if (c_index < size) - compressed[c_index] = 0; - if (c_index + 1 < size) - compressed[c_index + 1] = 0; -#endif // SANITIZER_WORDSIZE - - // debug-only code -#if 0 - StackTrace check_stack; - UncompressStack(&check_stack, compressed, size); - if (res < check_stack.size) { - Printf("res %zu check_stack.size %zu; c_size %zu\n", res, - check_stack.size, size); - } - // |res| may be greater than check_stack.size, because - // UncompressStack(CompressStack(stack)) eliminates the 0x0 frames. - CHECK(res >= check_stack.size); - CHECK_EQ(0, REAL(memcmp)(check_stack.trace, stack->trace, - check_stack.size * sizeof(uptr))); -#endif - - return res; +static bool MatchPc(uptr cur_pc, uptr trace_pc, uptr threshold) { + return cur_pc - trace_pc <= threshold || trace_pc - cur_pc <= threshold; } -SANITIZER_INTERFACE_ATTRIBUTE -void StackTrace::UncompressStack(StackTrace *stack, - u32 *compressed, uptr size) { -#if SANITIZER_WORDSIZE == 32 - // Don't uncompress, just copy. - stack->size = 0; - for (uptr i = 0; i < size && i < kStackTraceMax; i++) { - if (!compressed[i]) break; - stack->size++; - stack->trace[i] = compressed[i]; - } -#else // 64 bits, uncompress - uptr prev_pc = 0; - stack->size = 0; - for (uptr i = 0; i < size && stack->size < kStackTraceMax; i++) { - u32 x = compressed[i]; - uptr pc = 0; - if (x & (1U << 31)) { - // Printf("U co[%zu] offset: %x\n", i, x); - // this is an offset - s32 offset = x; - offset = (offset << 1) >> 1; // remove the 31-byte and sign-extend. - pc = prev_pc + offset; - CHECK(pc); - } else { - // CHECK(i + 1 < size); - if (i + 1 >= size) break; - uptr hi = x; - uptr lo = compressed[i+1]; - // Printf("U co[%zu] hi/lo: %zx %zx\n", i, hi, lo); - i++; - pc = (hi << 32) | lo; - if (!pc) break; - } - // Printf("U pc[%zu] %zx\n", stack->size, pc); - stack->trace[stack->size++] = pc; - prev_pc = pc; +uptr StackTrace::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 = 192; + for (uptr i = 0; i < size; ++i) { + if (MatchPc(pc, trace[i], kPcThreshold)) + return i; } -#endif // SANITIZER_WORDSIZE + return 0; } } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h index 1f05753..d06db5f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stacktrace.h +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace.h @@ -21,58 +21,55 @@ static const uptr kStackTraceMax = 256; defined(__powerpc__) || defined(__powerpc64__) || \ defined(__sparc__) || \ defined(__mips__)) -#define SANITIZER_CAN_FAST_UNWIND 0 +# define SANITIZER_CAN_FAST_UNWIND 0 +#elif SANITIZER_WINDOWS +# define SANITIZER_CAN_FAST_UNWIND 0 #else -#define SANITIZER_CAN_FAST_UNWIND 1 +# define SANITIZER_CAN_FAST_UNWIND 1 #endif struct StackTrace { typedef bool (*SymbolizeCallback)(const void *pc, char *out_buffer, int out_size); + uptr top_frame_bp; uptr size; - uptr max_size; uptr trace[kStackTraceMax]; + + // Prints a symbolized stacktrace, followed by an empty line. static void PrintStack(const uptr *addr, uptr size, - bool symbolize, const char *strip_file_prefix, - SymbolizeCallback symbolize_callback); - void CopyTo(uptr *dst, uptr dst_size) { - for (uptr i = 0; i < size && i < dst_size; i++) - dst[i] = trace[i]; - for (uptr i = size; i < dst_size; i++) - dst[i] = 0; - } + SymbolizeCallback symbolize_callback = 0); - void CopyFrom(uptr *src, uptr src_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++) { + for (uptr i = 0; i < size; i++) trace[i] = src[i]; - } } - void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom); - void SlowUnwindStack(uptr pc, uptr max_depth); + static bool WillUseFastUnwind(bool request_fast_unwind) { + // Check if fast unwind is available. Fast unwind is the only option on Mac. + if (!SANITIZER_CAN_FAST_UNWIND) + return false; + else if (SANITIZER_MAC) + return true; + return request_fast_unwind; + } - void PopStackFrames(uptr count); + void Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top, + uptr stack_bottom, bool request_fast_unwind); static uptr GetCurrentPc(); static uptr GetPreviousInstructionPc(uptr pc); - SANITIZER_INTERFACE_ATTRIBUTE - static uptr CompressStack(StackTrace *stack, - u32 *compressed, uptr size); - SANITIZER_INTERFACE_ATTRIBUTE - static void UncompressStack(StackTrace *stack, - u32 *compressed, uptr size); + private: + void FastUnwindStack(uptr pc, uptr bp, uptr stack_top, uptr stack_bottom, + uptr max_depth); + void SlowUnwindStack(uptr pc, uptr max_depth); + void PopStackFrames(uptr count); + uptr LocatePcInTrace(uptr pc); }; - -const char *StripPathPrefix(const char *filepath, - const char *strip_file_prefix); - -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom, bool fast); - } // namespace __sanitizer // Use this macro if you want to print stack trace with the caller diff --git a/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc new file mode 100644 index 0000000..ea2f9d0 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_stacktrace_libcdep.cc @@ -0,0 +1,26 @@ +//===-- sanitizer_stacktrace_libcdep.cc -----------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_stacktrace.h" + +namespace __sanitizer { + +void StackTrace::Unwind(uptr max_depth, uptr pc, uptr bp, uptr stack_top, + uptr stack_bottom, bool request_fast_unwind) { + if (!WillUseFastUnwind(request_fast_unwind)) + SlowUnwindStack(pc, max_depth); + else + FastUnwindStack(pc, bp, stack_top, stack_bottom, max_depth); + + top_frame_bp = size ? bp : 0; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc index 4f44a03..2b3dd29 100644 --- a/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_stoptheworld_linux_libcdep.cc @@ -16,6 +16,8 @@ #include "sanitizer_stoptheworld.h" +#include "sanitizer_platform_limits_posix.h" + #include #include // for CLONE_* definitions #include @@ -29,7 +31,16 @@ #endif #include // for signal-related stuff +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + #include "sanitizer_common.h" +#include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_linux.h" #include "sanitizer_mutex.h" @@ -45,30 +56,14 @@ // clone() interface (we want to share the address space with the caller // process, so we prefer clone() over fork()). // -// We avoid the use of libc for two reasons: +// We don't use any libc functions, relying instead on direct syscalls. There +// are two reasons for this: // 1. calling a library function while threads are suspended could cause a // deadlock, if one of the treads happens to be holding a libc lock; // 2. it's generally not safe to call libc functions from the tracer task, // because clone() does not set up a thread-local storage for it. Any // thread-local variables used by libc will be shared between the tracer task // and the thread which spawned it. -// -// We deal with this by replacing libc calls with calls to our own -// implementations defined in sanitizer_libc.h and sanitizer_linux.h. However, -// there are still some libc functions which are used here: -// -// * All of the system calls ultimately go through the libc syscall() function. -// We're operating under the assumption that syscall()'s implementation does -// not acquire any locks or use any thread-local data (except for the errno -// variable, which we handle separately). -// -// * We lack custom implementations of sigfillset() and sigaction(), so we use -// the libc versions instead. The same assumptions as above apply. -// -// * It is safe to call libc functions before the cloned thread is spawned or -// after it has exited. The following functions are used in this manner: -// sigdelset() -// sigprocmask() COMPILER_CHECK(sizeof(SuspendedThreadID) == sizeof(pid_t)); @@ -103,10 +98,11 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { &pterrno)) { // Either the thread is dead, or something prevented us from attaching. // Log this event and move on. - Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno); + if (common_flags()->verbosity) + Report("Could not attach to thread %d (errno %d).\n", thread_id, pterrno); return false; } else { - if (SanitizerVerbosity > 0) + if (common_flags()->verbosity) Report("Attached to thread %d.\n", thread_id); // The thread is not guaranteed to stop before ptrace returns, so we must // wait on it. @@ -116,8 +112,9 @@ bool ThreadSuspender::SuspendThread(SuspendedThreadID thread_id) { if (internal_iserror(waitpid_status, &wperrno)) { // Got a ECHILD error. I don't think this situation is possible, but it // doesn't hurt to report it. - Report("Waiting on thread %d failed, detaching (errno %d).\n", thread_id, - wperrno); + if (common_flags()->verbosity) + Report("Waiting on thread %d failed, detaching (errno %d).\n", + thread_id, wperrno); internal_ptrace(PTRACE_DETACH, thread_id, NULL, NULL); return false; } @@ -132,13 +129,14 @@ void ThreadSuspender::ResumeAllThreads() { int pterrno; if (!internal_iserror(internal_ptrace(PTRACE_DETACH, tid, NULL, NULL), &pterrno)) { - if (SanitizerVerbosity > 0) + if (common_flags()->verbosity) Report("Detached from thread %d.\n", tid); } else { // Either the thread is dead, or we are already detached. // The latter case is possible, for instance, if this function was called // from a signal handler. - Report("Could not detach from thread %d (errno %d).\n", tid, pterrno); + if (common_flags()->verbosity) + Report("Could not detach from thread %d (errno %d).\n", tid, pterrno); } } } @@ -183,15 +181,16 @@ static const int kUnblockedSignals[] = { SIGABRT, SIGILL, SIGFPE, SIGSEGV, struct TracerThreadArgument { StopTheWorldCallback callback; void *callback_argument; - // The tracer thread waits on this mutex while the parent finished its + // The tracer thread waits on this mutex while the parent finishes its // preparations. BlockingMutex mutex; + uptr parent_pid; }; static DieCallbackType old_die_callback; // Signal handler to wake up suspended threads when the tracer thread dies. -void TracerThreadSignalHandler(int signum, siginfo_t *siginfo, void *) { +void TracerThreadSignalHandler(int signum, void *siginfo, void *) { if (thread_suspender_instance != NULL) { if (signum == SIGABRT) thread_suspender_instance->KillAllThreads(); @@ -222,6 +221,11 @@ static int TracerThread(void* argument) { TracerThreadArgument *tracer_thread_argument = (TracerThreadArgument *)argument; + internal_prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); + // Check if parent is already dead. + if (internal_getppid() != tracer_thread_argument->parent_pid) + internal__exit(4); + // Wait for the parent thread to finish preparations. tracer_thread_argument->mutex.Lock(); tracer_thread_argument->mutex.Unlock(); @@ -244,17 +248,18 @@ static int TracerThread(void* argument) { // the mask we inherited from the caller thread. for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { - struct sigaction new_sigaction; + __sanitizer_kernel_sigaction_t new_sigaction; internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sa_sigaction = TracerThreadSignalHandler; + new_sigaction.sigaction = TracerThreadSignalHandler; new_sigaction.sa_flags = SA_ONSTACK | SA_SIGINFO; - sigfillset(&new_sigaction.sa_mask); - sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, NULL); } int exit_code = 0; if (!thread_suspender.SuspendAllThreads()) { - Report("Failed suspending threads.\n"); + if (common_flags()->verbosity) + Report("Failed suspending threads.\n"); exit_code = 3; } else { tracer_thread_argument->callback(thread_suspender.suspended_threads_list(), @@ -292,44 +297,36 @@ class ScopedStackSpaceWithGuard { uptr guard_start_; }; -NOINLINE static void WipeStack() { - char arr[256]; - internal_memset(arr, 0, sizeof(arr)); -} - // We have a limitation on the stack frame size, so some stuff had to be moved // into globals. -static sigset_t blocked_sigset; -static sigset_t old_sigset; -static struct sigaction old_sigactions[ARRAY_SIZE(kUnblockedSignals)]; +static __sanitizer_kernel_sigset_t blocked_sigset; +static __sanitizer_kernel_sigset_t old_sigset; +static __sanitizer_kernel_sigaction_t old_sigactions + [ARRAY_SIZE(kUnblockedSignals)]; class StopTheWorldScope { public: StopTheWorldScope() { - // Glibc's sigaction() has a side-effect where it copies garbage stack - // values into oldact, which can cause false negatives in LSan. As a quick - // workaround we zero some stack space here. - WipeStack(); // Block all signals that can be blocked safely, and install // default handlers for the remaining signals. // We cannot allow user-defined handlers to run while the ThreadSuspender // thread is active, because they could conceivably call some libc functions // which modify errno (which is shared between the two threads). - sigfillset(&blocked_sigset); + internal_sigfillset(&blocked_sigset); for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { // Remove the signal from the set of blocked signals. - sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); + internal_sigdelset(&blocked_sigset, kUnblockedSignals[signal_index]); // Install the default handler. - struct sigaction new_sigaction; + __sanitizer_kernel_sigaction_t new_sigaction; internal_memset(&new_sigaction, 0, sizeof(new_sigaction)); - new_sigaction.sa_handler = SIG_DFL; - sigfillset(&new_sigaction.sa_mask); - sigaction(kUnblockedSignals[signal_index], &new_sigaction, + new_sigaction.handler = SIG_DFL; + internal_sigfillset(&new_sigaction.sa_mask); + internal_sigaction(kUnblockedSignals[signal_index], &new_sigaction, &old_sigactions[signal_index]); } int sigprocmask_status = - sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); + internal_sigprocmask(SIG_BLOCK, &blocked_sigset, &old_sigset); CHECK_EQ(sigprocmask_status, 0); // sigprocmask should never fail // Make this process dumpable. Processes that are not dumpable cannot be // attached to. @@ -347,10 +344,10 @@ class StopTheWorldScope { // Restore the signal handlers. for (uptr signal_index = 0; signal_index < ARRAY_SIZE(kUnblockedSignals); signal_index++) { - sigaction(kUnblockedSignals[signal_index], + internal_sigaction(kUnblockedSignals[signal_index], &old_sigactions[signal_index], NULL); } - sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); + internal_sigprocmask(SIG_SETMASK, &old_sigset, &old_sigset); } private: @@ -363,6 +360,7 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { struct TracerThreadArgument tracer_thread_argument; tracer_thread_argument.callback = callback; tracer_thread_argument.callback_argument = argument; + tracer_thread_argument.parent_pid = internal_getpid(); const uptr kTracerStackSize = 2 * 1024 * 1024; ScopedStackSpaceWithGuard tracer_stack(kTracerStackSize); // Block the execution of TracerThread until after we have set ptrace @@ -375,7 +373,8 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { /* child_tidptr */); int local_errno = 0; if (internal_iserror(tracer_pid, &local_errno)) { - Report("Failed spawning a tracer thread (errno %d).\n", local_errno); + if (common_flags()->verbosity) + Report("Failed spawning a tracer thread (errno %d).\n", local_errno); tracer_thread_argument.mutex.Unlock(); } else { // On some systems we have to explicitly declare that we want to be traced @@ -390,8 +389,11 @@ void StopTheWorld(StopTheWorldCallback callback, void *argument) { // At this point, any signal will either be blocked or kill us, so waitpid // should never return (and set errno) while the tracer thread is alive. uptr waitpid_status = internal_waitpid(tracer_pid, NULL, __WALL); - if (internal_iserror(waitpid_status, &local_errno)) - Report("Waiting on the tracer thread failed (errno %d).\n", local_errno); + if (internal_iserror(waitpid_status, &local_errno)) { + if (common_flags()->verbosity) + Report("Waiting on the tracer thread failed (errno %d).\n", + local_errno); + } } } @@ -432,7 +434,8 @@ int SuspendedThreadsList::GetRegistersAndSP(uptr index, int pterrno; if (internal_iserror(internal_ptrace(PTRACE_GETREGS, tid, NULL, ®s), &pterrno)) { - Report("Could not get registers from thread %d (errno %d).\n", + if (common_flags()->verbosity) + Report("Could not get registers from thread %d (errno %d).\n", tid, pterrno); return -1; } diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.cc b/libsanitizer/sanitizer_common/sanitizer_suppressions.cc index d8e8976..14f13e6 100644 --- a/libsanitizer/sanitizer_common/sanitizer_suppressions.cc +++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.cc @@ -18,7 +18,7 @@ namespace __sanitizer { static const char *const kTypeStrings[SuppressionTypeCount] = { - "none", "race", "mutex", "thread", "signal", "leak" + "none", "race", "mutex", "thread", "signal", "leak", "called_from_lib" }; bool TemplateMatch(char *templ, const char *str) { @@ -127,10 +127,15 @@ void SuppressionContext::Parse(const char *str) { } } -uptr SuppressionContext::SuppressionCount() { +uptr SuppressionContext::SuppressionCount() const { return suppressions_.size(); } +const Suppression *SuppressionContext::SuppressionAt(uptr i) const { + CHECK_LT(i, suppressions_.size()); + return &suppressions_[i]; +} + void SuppressionContext::GetMatched( InternalMmapVector *matched) { for (uptr i = 0; i < suppressions_.size(); i++) diff --git a/libsanitizer/sanitizer_common/sanitizer_suppressions.h b/libsanitizer/sanitizer_common/sanitizer_suppressions.h index 9a0d87b..b4c719c 100644 --- a/libsanitizer/sanitizer_common/sanitizer_suppressions.h +++ b/libsanitizer/sanitizer_common/sanitizer_suppressions.h @@ -23,6 +23,7 @@ enum SuppressionType { SuppressionThread, SuppressionSignal, SuppressionLeak, + SuppressionLib, SuppressionTypeCount }; @@ -38,7 +39,8 @@ class SuppressionContext { SuppressionContext() : suppressions_(1), can_parse_(true) {} void Parse(const char *str); bool Match(const char* str, SuppressionType type, Suppression **s); - uptr SuppressionCount(); + uptr SuppressionCount() const; + const Suppression *SuppressionAt(uptr i) const; void GetMatched(InternalMmapVector *matched); private: @@ -50,7 +52,6 @@ class SuppressionContext { const char *SuppressionTypeString(SuppressionType t); -// Exposed for testing. bool TemplateMatch(char *templ, const char *str); } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc new file mode 100644 index 0000000..f417b08 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.cc @@ -0,0 +1,61 @@ +//===-- sanitizer_symbolizer.cc -------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#include "sanitizer_internal_defs.h" +#include "sanitizer_placement_new.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::symbolizer_; +StaticSpinMutex Symbolizer::init_mu_; +LowLevelAllocator Symbolizer::symbolizer_allocator_; + +Symbolizer *Symbolizer::GetOrNull() { + SpinMutexLock l(&init_mu_); + return symbolizer_; +} + +Symbolizer *Symbolizer::Get() { + SpinMutexLock l(&init_mu_); + RAW_CHECK_MSG(symbolizer_ != 0, "Using uninitialized symbolizer!"); + return symbolizer_; +} + +Symbolizer *Symbolizer::Disable() { + CHECK_EQ(0, symbolizer_); + // Initialize a dummy symbolizer. + symbolizer_ = new(symbolizer_allocator_) Symbolizer; + return symbolizer_; +} + +void Symbolizer::AddHooks(Symbolizer::StartSymbolizationHook start_hook, + Symbolizer::EndSymbolizationHook end_hook) { + CHECK(start_hook_ == 0 && end_hook_ == 0); + start_hook_ = start_hook; + end_hook_ = end_hook; +} + +Symbolizer::Symbolizer() : start_hook_(0), end_hook_(0) {} + +Symbolizer::SymbolizerScope::SymbolizerScope(const Symbolizer *sym) + : sym_(sym) { + if (sym_->start_hook_) + sym_->start_hook_(); +} + +Symbolizer::SymbolizerScope::~SymbolizerScope() { + if (sym_->end_hook_) + sym_->end_hook_(); +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h index 2c84773..af93de7 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer.h +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer.h @@ -5,19 +5,14 @@ // //===----------------------------------------------------------------------===// // -// Symbolizer is intended to be used by both -// AddressSanitizer and ThreadSanitizer to symbolize a given -// address. It is an analogue of addr2line utility and allows to map -// instruction address to a location in source code at run-time. +// Symbolizer is used by sanitizers to map instruction address to a location in +// source code at run-time. Symbolizer either uses __sanitizer_symbolize_* +// defined in the program, or (if they are missing) tries to find and +// launch "llvm-symbolizer" commandline tool in a separate process and +// communicate with it. // -// Symbolizer is planned to use debug information (in DWARF format) -// in a binary via interface defined in "llvm/DebugInfo/DIContext.h" -// -// Symbolizer code should be called from the run-time library of -// dynamic tools, and generally should not call memory allocation -// routines or other system library functions intercepted by those tools. -// Instead, Symbolizer code should use their replacements, defined in -// "compiler-rt/lib/sanitizer_common/sanitizer_libc.h". +// Generally we should try to avoid calling system library functions during +// symbolization (and use their replacements from sanitizer_libc.h instead). //===----------------------------------------------------------------------===// #ifndef SANITIZER_SYMBOLIZER_H #define SANITIZER_SYMBOLIZER_H @@ -25,7 +20,6 @@ #include "sanitizer_allocator_internal.h" #include "sanitizer_internal_defs.h" #include "sanitizer_libc.h" -// WARNING: Do not include system headers here. See details above. namespace __sanitizer { @@ -67,8 +61,24 @@ struct DataInfo { uptr size; }; -class SymbolizerInterface { +class Symbolizer { public: + /// Returns platform-specific implementation of Symbolizer. The symbolizer + /// must be initialized (with init or disable) before calling this function. + static Symbolizer *Get(); + /// Returns platform-specific implementation of Symbolizer, or null if not + /// initialized. + static Symbolizer *GetOrNull(); + /// Returns platform-specific implementation of Symbolizer. Will + /// automatically initialize symbolizer as if by calling Init(0) if needed. + static Symbolizer *GetOrInit(); + /// Initialize and return the symbolizer, given an optional path to an + /// external symbolizer. The path argument is only required for legacy + /// reasons as this function will check $PATH for an external symbolizer. Not + /// thread safe. + static Symbolizer *Init(const char* path_to_external = 0); + /// Initialize the symbolizer in a disabled state. Not thread safe. + static Symbolizer *Disable(); // Fills at most "max_frames" elements of "frames" with descriptions // for a given address (in all inlined functions). Returns the number // of descriptions actually filled. @@ -82,6 +92,9 @@ class SymbolizerInterface { virtual bool IsAvailable() { return false; } + virtual bool IsExternalAvailable() { + return false; + } // Release internal caches (if any). virtual void Flush() {} // Attempts to demangle the provided C++ mangled name. @@ -89,17 +102,42 @@ class SymbolizerInterface { return name; } virtual void PrepareForSandboxing() {} - // Starts external symbolizer program in a subprocess. Sanitizer communicates - // with external symbolizer via pipes. If path_to_symbolizer is NULL or empty, - // tries to look for llvm-symbolizer in PATH. - virtual bool InitializeExternal(const char *path_to_symbolizer) { - return false; - } -}; -// Returns platform-specific implementation of SymbolizerInterface. It can't be -// used from multiple threads simultaneously. -SANITIZER_WEAK_ATTRIBUTE SymbolizerInterface *getSymbolizer(); + // Allow user to install hooks that would be called before/after Symbolizer + // does the actual file/line info fetching. Specific sanitizers may need this + // to distinguish system library calls made in user code from calls made + // during in-process symbolization. + typedef void (*StartSymbolizationHook)(); + typedef void (*EndSymbolizationHook)(); + // May be called at most once. + void AddHooks(StartSymbolizationHook start_hook, + EndSymbolizationHook end_hook); + + private: + /// Platform-specific function for creating a Symbolizer object. + static Symbolizer *PlatformInit(const char *path_to_external); + /// Create a symbolizer and store it to symbolizer_ without checking if one + /// already exists. Not thread safe. + static Symbolizer *CreateAndStore(const char *path_to_external); + + static Symbolizer *symbolizer_; + static StaticSpinMutex init_mu_; + + protected: + Symbolizer(); + + static LowLevelAllocator symbolizer_allocator_; + + StartSymbolizationHook start_hook_; + EndSymbolizationHook end_hook_; + class SymbolizerScope { + public: + explicit SymbolizerScope(const Symbolizer *sym); + ~SymbolizerScope(); + private: + const Symbolizer *sym_; + }; +}; } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc new file mode 100644 index 0000000..5654d1d --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.cc @@ -0,0 +1,144 @@ +//===-- sanitizer_symbolizer_libbacktrace.cc ------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Libbacktrace implementation of symbolizer parts. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" + +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_libbacktrace.h" + +#if SANITIZER_LIBBACKTRACE +# include "backtrace-supported.h" +# if SANITIZER_POSIX && BACKTRACE_SUPPORTED && !BACKTRACE_USES_MALLOC +# include "backtrace.h" +# else +# define SANITIZER_LIBBACKTRACE 0 +# endif +#endif + +namespace __sanitizer { + +#if SANITIZER_LIBBACKTRACE + +namespace { + +struct SymbolizeCodeData { + AddressInfo *frames; + uptr n_frames; + uptr max_frames; + const char *module_name; + uptr module_offset; +}; + +extern "C" { +static int SymbolizeCodePCInfoCallback(void *vdata, uintptr_t addr, + const char *filename, int lineno, + const char *function) { + SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata; + if (function) { + AddressInfo *info = &cdata->frames[cdata->n_frames++]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, cdata->module_name, + cdata->module_offset); + info->function = internal_strdup(function); + if (filename) + info->file = internal_strdup(filename); + info->line = lineno; + if (cdata->n_frames == cdata->max_frames) + return 1; + } + return 0; +} + +static void SymbolizeCodeCallback(void *vdata, uintptr_t addr, + const char *symname, uintptr_t, uintptr_t) { + SymbolizeCodeData *cdata = (SymbolizeCodeData *)vdata; + if (symname) { + AddressInfo *info = &cdata->frames[0]; + info->Clear(); + info->FillAddressAndModuleInfo(addr, cdata->module_name, + cdata->module_offset); + info->function = internal_strdup(symname); + cdata->n_frames = 1; + } +} + +static void SymbolizeDataCallback(void *vdata, uintptr_t, const char *symname, + uintptr_t symval, uintptr_t symsize) { + DataInfo *info = (DataInfo *)vdata; + if (symname && symval) { + info->name = internal_strdup(symname); + info->start = symval; + info->size = symsize; + } +} + +static void ErrorCallback(void *, const char *, int) {} +} // extern "C" + +} // namespace + +LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { + // State created in backtrace_create_state is leaked. + void *state = (void *)(backtrace_create_state("/proc/self/exe", 0, + ErrorCallback, NULL)); + if (!state) + return 0; + return new(*alloc) LibbacktraceSymbolizer(state); +} + +uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames, + uptr max_frames, + const char *module_name, + uptr module_offset) { + SymbolizeCodeData data; + data.frames = frames; + data.n_frames = 0; + data.max_frames = max_frames; + data.module_name = module_name; + data.module_offset = module_offset; + backtrace_pcinfo((backtrace_state *)state_, addr, SymbolizeCodePCInfoCallback, + ErrorCallback, &data); + if (data.n_frames) + return data.n_frames; + backtrace_syminfo((backtrace_state *)state_, addr, SymbolizeCodeCallback, + ErrorCallback, &data); + return data.n_frames; +} + +bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { + backtrace_syminfo((backtrace_state *)state_, info->address, + SymbolizeDataCallback, ErrorCallback, info); + return true; +} + +#else // SANITIZER_LIBBACKTRACE + +LibbacktraceSymbolizer *LibbacktraceSymbolizer::get(LowLevelAllocator *alloc) { + return 0; +} + +uptr LibbacktraceSymbolizer::SymbolizeCode(uptr addr, AddressInfo *frames, + uptr max_frames, + const char *module_name, + uptr module_offset) { + (void)state_; + return 0; +} + +bool LibbacktraceSymbolizer::SymbolizeData(DataInfo *info) { + return false; +} + +#endif // SANITIZER_LIBBACKTRACE + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h new file mode 100644 index 0000000..f09cea5 --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libbacktrace.h @@ -0,0 +1,38 @@ +//===-- sanitizer_symbolizer_libbacktrace.h -------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +// Header for libbacktrace symbolizer. +//===----------------------------------------------------------------------===// + +#include "sanitizer_platform.h" +#include "sanitizer_common.h" +#include "sanitizer_symbolizer.h" + +#ifndef SANITIZER_LIBBACKTRACE +# define SANITIZER_LIBBACKTRACE 0 +#endif + +namespace __sanitizer { + +class LibbacktraceSymbolizer { + public: + static LibbacktraceSymbolizer *get(LowLevelAllocator *alloc); + + uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames, + const char *module_name, uptr module_offset); + + bool SymbolizeData(DataInfo *info); + + private: + explicit LibbacktraceSymbolizer(void *state) : state_(state) {} + + void *state_; // Leaked. +}; + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc new file mode 100644 index 0000000..2d9caaf --- /dev/null +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_libcdep.cc @@ -0,0 +1,37 @@ +//===-- sanitizer_symbolizer_libcdep.cc -----------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is shared between AddressSanitizer and ThreadSanitizer +// run-time libraries. +//===----------------------------------------------------------------------===// + +#include "sanitizer_internal_defs.h" +#include "sanitizer_symbolizer.h" + +namespace __sanitizer { + +Symbolizer *Symbolizer::CreateAndStore(const char *path_to_external) { + Symbolizer *platform_symbolizer = PlatformInit(path_to_external); + if (!platform_symbolizer) + return Disable(); + symbolizer_ = platform_symbolizer; + return platform_symbolizer; +} + +Symbolizer *Symbolizer::Init(const char *path_to_external) { + CHECK_EQ(0, symbolizer_); + return CreateAndStore(path_to_external); +} + +Symbolizer *Symbolizer::GetOrInit() { + SpinMutexLock l(&init_mu_); + if (symbolizer_ == 0) + return CreateAndStore(0); + return symbolizer_; +} + +} // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc index 3406308..9f34b64 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_posix_libcdep.cc @@ -19,6 +19,7 @@ #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" #include "sanitizer_symbolizer.h" +#include "sanitizer_symbolizer_libbacktrace.h" #include #include @@ -203,19 +204,49 @@ static const char *ExtractUptr(const char *str, const char *delims, // :: // ... // +// ExternalSymbolizer may not be used from two threads simultaneously. class ExternalSymbolizer { public: - ExternalSymbolizer(const char *path, int input_fd, int output_fd) + explicit ExternalSymbolizer(const char *path) : path_(path), - input_fd_(input_fd), - output_fd_(output_fd), - times_restarted_(0) { + input_fd_(kInvalidFd), + output_fd_(kInvalidFd), + times_restarted_(0), + failed_to_start_(false) { CHECK(path_); - CHECK_NE(input_fd_, kInvalidFd); - CHECK_NE(output_fd_, kInvalidFd); + CHECK_NE(path[0], '\0'); } char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + for (; times_restarted_ < kMaxTimesRestarted; times_restarted_++) { + // Start or restart symbolizer if we failed to send command to it. + if (char *res = SendCommandImpl(is_data, module_name, module_offset)) + return res; + Restart(); + } + if (!failed_to_start_) { + Report("WARNING: Failed to use and restart external symbolizer!\n"); + failed_to_start_ = true; + } + return 0; + } + + void Flush() { + } + + private: + bool Restart() { + if (input_fd_ != kInvalidFd) + internal_close(input_fd_); + if (output_fd_ != kInvalidFd) + internal_close(output_fd_); + return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); + } + + char *SendCommandImpl(bool is_data, const char *module_name, + uptr module_offset) { + if (input_fd_ == kInvalidFd || output_fd_ == kInvalidFd) + return 0; CHECK(module_name); internal_snprintf(buffer_, kBufferSize, "%s\"%s\" 0x%zx\n", is_data ? "DATA " : "", module_name, module_offset); @@ -226,18 +257,6 @@ class ExternalSymbolizer { return buffer_; } - bool Restart() { - if (times_restarted_ >= kMaxTimesRestarted) return false; - times_restarted_++; - internal_close(input_fd_); - internal_close(output_fd_); - return StartSymbolizerSubprocess(path_, &input_fd_, &output_fd_); - } - - void Flush() { - } - - private: bool readFromSymbolizer(char *buffer, uptr max_length) { if (max_length == 0) return true; @@ -281,10 +300,9 @@ class ExternalSymbolizer { static const uptr kMaxTimesRestarted = 5; uptr times_restarted_; + bool failed_to_start_; }; -static LowLevelAllocator symbolizer_allocator; // Linker initialized. - #if SANITIZER_SUPPORTS_WEAK_HOOKS extern "C" { SANITIZER_INTERFACE_ATTRIBUTE SANITIZER_WEAK_ATTRIBUTE @@ -304,11 +322,10 @@ class InternalSymbolizer { public: typedef bool (*SanitizerSymbolizeFn)(const char*, u64, char*, int); - static InternalSymbolizer *get() { + static InternalSymbolizer *get(LowLevelAllocator *alloc) { if (__sanitizer_symbolize_code != 0 && __sanitizer_symbolize_data != 0) { - void *mem = symbolizer_allocator.Allocate(sizeof(InternalSymbolizer)); - return new(mem) InternalSymbolizer(); + return new(*alloc) InternalSymbolizer(); } return 0; } @@ -355,7 +372,7 @@ class InternalSymbolizer { class InternalSymbolizer { public: - static InternalSymbolizer *get() { return 0; } + static InternalSymbolizer *get(LowLevelAllocator *alloc) { return 0; } char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { return 0; } @@ -365,11 +382,18 @@ class InternalSymbolizer { #endif // SANITIZER_SUPPORTS_WEAK_HOOKS -class Symbolizer : public SymbolizerInterface { - // This class has no constructor, as global constructors are forbidden in - // sanitizer_common. It should be linker initialized instead. +class POSIXSymbolizer : public Symbolizer { public: + POSIXSymbolizer(ExternalSymbolizer *external_symbolizer, + InternalSymbolizer *internal_symbolizer, + LibbacktraceSymbolizer *libbacktrace_symbolizer) + : Symbolizer(), + external_symbolizer_(external_symbolizer), + internal_symbolizer_(internal_symbolizer), + libbacktrace_symbolizer_(libbacktrace_symbolizer) {} + uptr SymbolizeCode(uptr addr, AddressInfo *frames, uptr max_frames) { + BlockingMutexLock l(&mu_); if (max_frames == 0) return 0; LoadedModule *module = FindModuleForAddress(addr); @@ -377,9 +401,17 @@ class Symbolizer : public SymbolizerInterface { return 0; const char *module_name = module->full_name(); uptr module_offset = addr - module->base_address(); + // First, try to use libbacktrace symbolizer (if it's available). + if (libbacktrace_symbolizer_ != 0) { + mu_.CheckLocked(); + uptr res = libbacktrace_symbolizer_->SymbolizeCode( + addr, frames, max_frames, module_name, module_offset); + if (res > 0) + return res; + } const char *str = SendCommand(false, module_name, module_offset); if (str == 0) { - // External symbolizer was not initialized or failed. Fill only data + // Symbolizer was not initialized or failed. Fill only data // about module name and offset. AddressInfo *info = &frames[0]; info->Clear(); @@ -430,6 +462,7 @@ class Symbolizer : public SymbolizerInterface { } bool SymbolizeData(uptr addr, DataInfo *info) { + BlockingMutexLock l(&mu_); LoadedModule *module = FindModuleForAddress(addr); if (module == 0) return false; @@ -439,6 +472,11 @@ class Symbolizer : public SymbolizerInterface { info->address = addr; info->module = internal_strdup(module_name); info->module_offset = module_offset; + if (libbacktrace_symbolizer_ != 0) { + mu_.CheckLocked(); + if (libbacktrace_symbolizer_->SymbolizeData(info)) + return true; + } const char *str = SendCommand(true, module_name, module_offset); if (str == 0) return true; @@ -449,42 +487,38 @@ class Symbolizer : public SymbolizerInterface { return true; } - bool InitializeExternal(const char *path_to_symbolizer) { - if (!path_to_symbolizer || path_to_symbolizer[0] == '\0') { - path_to_symbolizer = FindPathToBinary("llvm-symbolizer"); - if (!path_to_symbolizer) - return false; - } - int input_fd, output_fd; - if (!StartSymbolizerSubprocess(path_to_symbolizer, &input_fd, &output_fd)) - return false; - void *mem = symbolizer_allocator.Allocate(sizeof(ExternalSymbolizer)); - external_symbolizer_ = new(mem) ExternalSymbolizer(path_to_symbolizer, - input_fd, output_fd); - return true; + bool IsAvailable() { + return internal_symbolizer_ != 0 || external_symbolizer_ != 0 || + libbacktrace_symbolizer_ != 0; } - bool IsAvailable() { - if (internal_symbolizer_ == 0) - internal_symbolizer_ = InternalSymbolizer::get(); - return internal_symbolizer_ || external_symbolizer_; + bool IsExternalAvailable() { + return external_symbolizer_ != 0; } void Flush() { - if (internal_symbolizer_) + BlockingMutexLock l(&mu_); + if (internal_symbolizer_ != 0) { + SymbolizerScope sym_scope(this); internal_symbolizer_->Flush(); - if (external_symbolizer_) + } + if (external_symbolizer_ != 0) external_symbolizer_->Flush(); } const char *Demangle(const char *name) { - if (IsAvailable() && internal_symbolizer_ != 0) + BlockingMutexLock l(&mu_); + // Run hooks even if we don't use internal symbolizer, as cxxabi + // demangle may call system functions. + SymbolizerScope sym_scope(this); + if (internal_symbolizer_ != 0) return internal_symbolizer_->Demangle(name); return DemangleCXXABI(name); } void PrepareForSandboxing() { #if SANITIZER_LINUX && !SANITIZER_ANDROID + BlockingMutexLock l(&mu_); // Cache /proc/self/exe on Linux. CacheBinaryName(); #endif @@ -492,41 +526,26 @@ class Symbolizer : public SymbolizerInterface { private: char *SendCommand(bool is_data, const char *module_name, uptr module_offset) { + mu_.CheckLocked(); // First, try to use internal symbolizer. - if (!IsAvailable()) { - return 0; - } if (internal_symbolizer_) { + SymbolizerScope sym_scope(this); return internal_symbolizer_->SendCommand(is_data, module_name, module_offset); } // Otherwise, fall back to external symbolizer. - if (external_symbolizer_ == 0) { - ReportExternalSymbolizerError( - "WARNING: Trying to symbolize code, but external " - "symbolizer is not initialized!\n"); - return 0; - } - for (;;) { - char *reply = external_symbolizer_->SendCommand(is_data, module_name, - module_offset); - if (reply) - return reply; - // Try to restart symbolizer subprocess. If we don't succeed, forget - // about it and don't try to use it later. - if (!external_symbolizer_->Restart()) { - ReportExternalSymbolizerError( - "WARNING: Failed to use and restart external symbolizer!\n"); - external_symbolizer_ = 0; - return 0; - } + if (external_symbolizer_) { + return external_symbolizer_->SendCommand(is_data, module_name, + module_offset); } + return 0; } LoadedModule *FindModuleForAddress(uptr address) { + mu_.CheckLocked(); bool modules_were_reloaded = false; if (modules_ == 0 || !modules_fresh_) { - modules_ = (LoadedModule*)(symbolizer_allocator.Allocate( + modules_ = (LoadedModule*)(symbolizer_allocator_.Allocate( kMaxNumberOfModuleContexts * sizeof(LoadedModule))); CHECK(modules_); n_modules_ = GetListOfModules(modules_, kMaxNumberOfModuleContexts, @@ -553,41 +572,40 @@ class Symbolizer : public SymbolizerInterface { return 0; } - void ReportExternalSymbolizerError(const char *msg) { - // Don't use atomics here for now, as SymbolizeCode can't be called - // from multiple threads anyway. - static bool reported; - if (!reported) { - Report(msg); - reported = true; - } - } - // 16K loaded modules should be enough for everyone. static const uptr kMaxNumberOfModuleContexts = 1 << 14; LoadedModule *modules_; // Array of module descriptions is leaked. uptr n_modules_; // If stale, need to reload the modules before looking up addresses. bool modules_fresh_; + BlockingMutex mu_; - ExternalSymbolizer *external_symbolizer_; // Leaked. - InternalSymbolizer *internal_symbolizer_; // Leaked. + ExternalSymbolizer *external_symbolizer_; // Leaked. + InternalSymbolizer *const internal_symbolizer_; // Leaked. + LibbacktraceSymbolizer *libbacktrace_symbolizer_; // Leaked. }; -static ALIGNED(64) char symbolizer_placeholder[sizeof(Symbolizer)]; -static Symbolizer *symbolizer; - -SymbolizerInterface *getSymbolizer() { - static atomic_uint8_t initialized; - static StaticSpinMutex init_mu; - if (atomic_load(&initialized, memory_order_acquire) == 0) { - SpinMutexLock l(&init_mu); - if (atomic_load(&initialized, memory_order_relaxed) == 0) { - symbolizer = new(symbolizer_placeholder) Symbolizer(); - atomic_store(&initialized, 1, memory_order_release); +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { + InternalSymbolizer* internal_symbolizer = + InternalSymbolizer::get(&symbolizer_allocator_); + ExternalSymbolizer *external_symbolizer = 0; + LibbacktraceSymbolizer *libbacktrace_symbolizer = 0; + + if (!internal_symbolizer) { + libbacktrace_symbolizer = + LibbacktraceSymbolizer::get(&symbolizer_allocator_); + if (!libbacktrace_symbolizer) { + // Find path to llvm-symbolizer if it's not provided. + if (!path_to_external) + path_to_external = FindPathToBinary("llvm-symbolizer"); + if (path_to_external && path_to_external[0] != '\0') + external_symbolizer = new(symbolizer_allocator_) + ExternalSymbolizer(path_to_external); } } - return symbolizer; + + return new(symbolizer_allocator_) POSIXSymbolizer( + external_symbolizer, internal_symbolizer, libbacktrace_symbolizer); } } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc index 44801f3..446de8a 100644 --- a/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_symbolizer_win.cc @@ -12,16 +12,11 @@ #include "sanitizer_platform.h" #if SANITIZER_WINDOWS -#include "sanitizer_internal_defs.h" #include "sanitizer_symbolizer.h" namespace __sanitizer { -static SymbolizerInterface win_symbolizer; // Linker initialized. - -SymbolizerInterface *getSymbolizer() { - return &win_symbolizer; -} +Symbolizer *Symbolizer::PlatformInit(const char *path_to_external) { return 0; } } // namespace __sanitizer diff --git a/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc index 4f405d9..8810c7f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc +++ b/libsanitizer/sanitizer_common/sanitizer_syscall_linux_x86_64.inc @@ -11,7 +11,8 @@ static uptr internal_syscall(u64 nr) { u64 retval; - asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11"); + asm volatile("syscall" : "=a"(retval) : "a"(nr) : "rcx", "r11", + "memory", "cc"); return retval; } @@ -19,7 +20,7 @@ template static uptr internal_syscall(u64 nr, T1 arg1) { u64 retval; asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1) : - "rcx", "r11"); + "rcx", "r11", "memory", "cc"); return retval; } @@ -27,7 +28,7 @@ template static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2) { u64 retval; asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), - "S"((u64)arg2) : "rcx", "r11"); + "S"((u64)arg2) : "rcx", "r11", "memory", "cc"); return retval; } @@ -35,7 +36,7 @@ template static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3) { u64 retval; asm volatile("syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), - "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11"); + "S"((u64)arg2), "d"((u64)arg3) : "rcx", "r11", "memory", "cc"); return retval; } @@ -45,7 +46,7 @@ static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4) { asm volatile("mov %5, %%r10;" "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4) : - "rcx", "r11", "r10"); + "rcx", "r11", "r10", "memory", "cc"); return retval; } @@ -57,7 +58,7 @@ static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, "mov %6, %%r8;" "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5) : - "rcx", "r11", "r10", "r8"); + "rcx", "r11", "r10", "r8", "memory", "cc"); return retval; } @@ -71,7 +72,8 @@ static uptr internal_syscall(u64 nr, T1 arg1, T2 arg2, T3 arg3, T4 arg4, "mov %7, %%r9;" "syscall" : "=a"(retval) : "a"(nr), "D"((u64)arg1), "S"((u64)arg2), "d"((u64)arg3), "r"((u64)arg4), "r"((u64)arg5), - "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9"); + "r"((u64)arg6) : "rcx", "r11", "r10", "r8", "r9", + "memory", "cc"); return retval; } diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc index e639430..666955f 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.cc @@ -198,6 +198,18 @@ void ThreadRegistry::SetThreadName(u32 tid, const char *name) { tctx->SetName(name); } +void ThreadRegistry::SetThreadNameByUserId(uptr user_id, const char *name) { + BlockingMutexLock l(&mtx_); + for (u32 tid = 0; tid < n_contexts_; tid++) { + ThreadContextBase *tctx = threads_[tid]; + if (tctx != 0 && tctx->user_id == user_id && + tctx->status != ThreadStatusInvalid) { + tctx->SetName(name); + return; + } + } +} + void ThreadRegistry::DetachThread(u32 tid) { BlockingMutexLock l(&mtx_); CHECK_LT(tid, n_contexts_); diff --git a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h index 1ae47b8..81c2709 100644 --- a/libsanitizer/sanitizer_common/sanitizer_thread_registry.h +++ b/libsanitizer/sanitizer_common/sanitizer_thread_registry.h @@ -107,6 +107,7 @@ class ThreadRegistry { ThreadContextBase *FindThreadContextByOsIDLocked(uptr os_id); void SetThreadName(u32 tid, const char *name); + void SetThreadNameByUserId(uptr user_id, const char *name); void DetachThread(u32 tid); void JoinThread(u32 tid, void *arg); void FinishThread(u32 tid); diff --git a/libsanitizer/sanitizer_common/sanitizer_win.cc b/libsanitizer/sanitizer_common/sanitizer_win.cc index 0a5fe81..c48274e 100644 --- a/libsanitizer/sanitizer_common/sanitizer_win.cc +++ b/libsanitizer/sanitizer_common/sanitizer_win.cc @@ -213,7 +213,7 @@ u64 NanoTime() { void Abort() { abort(); - _exit(-1); // abort is not NORETURN on Windows. + internal__exit(-1); // abort is not NORETURN on Windows. } uptr GetListOfModules(LoadedModule *modules, uptr max_modules, @@ -303,7 +303,7 @@ uptr internal_sched_yield() { } void internal__exit(int exitcode) { - _exit(exitcode); + ExitProcess(exitcode); } // ---------------------- BlockingMutex ---------------- {{{1 @@ -374,31 +374,15 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size, #endif } -void GetStackTrace(StackTrace *stack, uptr max_s, uptr pc, uptr bp, - uptr stack_top, uptr stack_bottom, bool fast) { - (void)fast; - (void)stack_top; - (void)stack_bottom; - stack->max_size = max_s; - void *tmp[kStackTraceMax]; - +void StackTrace::SlowUnwindStack(uptr pc, uptr max_depth) { // FIXME: CaptureStackBackTrace might be too slow for us. // FIXME: Compare with StackWalk64. // FIXME: Look at LLVMUnhandledExceptionFilter in Signals.inc - uptr cs_ret = CaptureStackBackTrace(1, stack->max_size, tmp, 0); - uptr offset = 0; + size = CaptureStackBackTrace(2, Min(max_depth, kStackTraceMax), + (void**)trace, 0); // Skip the RTL frames by searching for the PC in the stacktrace. - // FIXME: this doesn't work well for the malloc/free stacks yet. - for (uptr i = 0; i < cs_ret; i++) { - if (pc != (uptr)tmp[i]) - continue; - offset = i; - break; - } - - stack->size = cs_ret - offset; - for (uptr i = 0; i < stack->size; i++) - stack->trace[i] = (uptr)tmp[i + offset]; + uptr pc_location = LocatePcInTrace(pc); + PopStackFrames(pc_location); } void MaybeOpenReportFile() { diff --git a/libsanitizer/tsan/Makefile.am b/libsanitizer/tsan/Makefile.am index 9b039d2..ca41550 100644 --- a/libsanitizer/tsan/Makefile.am +++ b/libsanitizer/tsan/Makefile.am @@ -23,6 +23,7 @@ tsan_files = \ tsan_rtl.cc \ tsan_stat.cc \ tsan_sync.cc \ + tsan_ignoreset.cc \ tsan_interceptors.cc \ tsan_md5.cc \ tsan_platform_mac.cc \ diff --git a/libsanitizer/tsan/Makefile.in b/libsanitizer/tsan/Makefile.in index eb5c7e7..0bd1608 100644 --- a/libsanitizer/tsan/Makefile.in +++ b/libsanitizer/tsan/Makefile.in @@ -84,12 +84,12 @@ libtsan_la_DEPENDENCIES = \ am__objects_1 = tsan_clock.lo tsan_interface_atomic.lo tsan_mutex.lo \ tsan_report.lo tsan_rtl_thread.lo tsan_symbolize.lo \ tsan_flags.lo tsan_interface.lo tsan_platform_linux.lo \ - tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_interceptors.lo \ - tsan_md5.lo tsan_platform_mac.lo tsan_rtl_mutex.lo \ - tsan_suppressions.lo tsan_interface_ann.lo tsan_mman.lo \ - tsan_rtl_report.lo tsan_fd.lo tsan_interface_java.lo \ - tsan_mutexset.lo tsan_symbolize_addr2line_linux.lo \ - tsan_rtl_amd64.lo + tsan_rtl.lo tsan_stat.lo tsan_sync.lo tsan_ignoreset.lo \ + tsan_interceptors.lo tsan_md5.lo tsan_platform_mac.lo \ + tsan_rtl_mutex.lo tsan_suppressions.lo tsan_interface_ann.lo \ + tsan_mman.lo tsan_rtl_report.lo tsan_fd.lo \ + tsan_interface_java.lo tsan_mutexset.lo \ + tsan_symbolize_addr2line_linux.lo tsan_rtl_amd64.lo am_libtsan_la_OBJECTS = $(am__objects_1) libtsan_la_OBJECTS = $(am_libtsan_la_OBJECTS) libtsan_la_LINK = $(LIBTOOL) --tag=CXX $(AM_LIBTOOLFLAGS) \ @@ -283,6 +283,7 @@ tsan_files = \ tsan_rtl.cc \ tsan_stat.cc \ tsan_sync.cc \ + tsan_ignoreset.cc \ tsan_interceptors.cc \ tsan_md5.cc \ tsan_platform_mac.cc \ @@ -417,6 +418,7 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_clock.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_fd.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_flags.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_ignoreset.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interceptors.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface.Plo@am__quote@ @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tsan_interface_ann.Plo@am__quote@ diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index 68936e0..3f20797 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -39,10 +39,8 @@ const int kTidBits = 13; const unsigned kMaxTid = 1 << kTidBits; const unsigned kMaxTidInClock = kMaxTid * 2; // This includes msb 'freed' bit. const int kClkBits = 42; -#ifndef TSAN_GO -const int kShadowStackSize = 4 * 1024; -const int kTraceStackSize = 256; -#endif +const uptr kShadowStackSize = 64 * 1024; +const uptr kTraceStackSize = 256; #ifdef TSAN_SHADOW_COUNT # if TSAN_SHADOW_COUNT == 2 \ @@ -154,6 +152,7 @@ struct MD5Hash { MD5Hash md5_hash(const void *data, uptr size); struct ThreadState; +class ThreadContext; struct Context; struct ReportStack; class ReportDesc; diff --git a/libsanitizer/tsan/tsan_fd.cc b/libsanitizer/tsan/tsan_fd.cc index de852b1..b7ac311 100644 --- a/libsanitizer/tsan/tsan_fd.cc +++ b/libsanitizer/tsan/tsan_fd.cc @@ -40,6 +40,11 @@ struct FdContext { static FdContext fdctx; +static bool bogusfd(int fd) { + // Apparently a bogus fd value. + return fd < 0 || fd >= kTableSize; +} + static FdSync *allocsync() { FdSync *s = (FdSync*)internal_alloc(MBlockFD, sizeof(FdSync)); atomic_store(&s->rc, 1, memory_order_relaxed); @@ -67,6 +72,7 @@ static void unref(ThreadState *thr, uptr pc, FdSync *s) { } static FdDesc *fddesc(ThreadState *thr, uptr pc, int fd) { + CHECK_GE(fd, 0); CHECK_LT(fd, kTableSize); atomic_uintptr_t *pl1 = &fdctx.tab[fd / kTableSizeL2]; uptr l1 = atomic_load(pl1, memory_order_consume); @@ -146,6 +152,8 @@ bool FdLocation(uptr addr, int *fd, int *tid, u32 *stack) { } void FdAcquire(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdAcquire(%d) -> %p\n", thr->tid, fd, s); @@ -155,6 +163,8 @@ void FdAcquire(ThreadState *thr, uptr pc, int fd) { } void FdRelease(ThreadState *thr, uptr pc, int fd) { + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); FdSync *s = d->sync; DPrintf("#%d: FdRelease(%d) -> %p\n", thr->tid, fd, s); @@ -165,12 +175,16 @@ void FdRelease(ThreadState *thr, uptr pc, int fd) { void FdAccess(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdAccess(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); MemoryRead(thr, pc, (uptr)d, kSizeLog8); } void FdClose(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdClose(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; FdDesc *d = fddesc(thr, pc, fd); // To catch races between fd usage and close. MemoryWrite(thr, pc, (uptr)d, kSizeLog8); @@ -185,11 +199,15 @@ void FdClose(ThreadState *thr, uptr pc, int fd) { void FdFileCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdFileCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.filesync); } void FdDup(ThreadState *thr, uptr pc, int oldfd, int newfd) { DPrintf("#%d: FdDup(%d, %d)\n", thr->tid, oldfd, newfd); + if (bogusfd(oldfd) || bogusfd(newfd)) + return; // Ignore the case when user dups not yet connected socket. FdDesc *od = fddesc(thr, pc, oldfd); MemoryRead(thr, pc, (uptr)od, kSizeLog8); @@ -207,32 +225,44 @@ void FdPipeCreate(ThreadState *thr, uptr pc, int rfd, int wfd) { void FdEventCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdEventCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSignalCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSignalCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdInotifyCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdInotifyCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, 0); } void FdPollCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdPollCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, allocsync()); } void FdSocketCreate(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketCreate(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // It can be a UDP socket. init(thr, pc, fd, &fdctx.socksync); } void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { DPrintf("#%d: FdSocketAccept(%d, %d)\n", thr->tid, fd, newfd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Acquire(thr, pc, (uptr)&fdctx.connectsync); init(thr, pc, newfd, &fdctx.socksync); @@ -240,12 +270,16 @@ void FdSocketAccept(ThreadState *thr, uptr pc, int fd, int newfd) { void FdSocketConnecting(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnecting(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; // Synchronize connect->accept. Release(thr, pc, (uptr)&fdctx.connectsync); } void FdSocketConnect(ThreadState *thr, uptr pc, int fd) { DPrintf("#%d: FdSocketConnect(%d)\n", thr->tid, fd); + if (bogusfd(fd)) + return; init(thr, pc, fd, &fdctx.socksync); } diff --git a/libsanitizer/tsan/tsan_flags.cc b/libsanitizer/tsan/tsan_flags.cc index dbde421..5bcf42e 100644 --- a/libsanitizer/tsan/tsan_flags.cc +++ b/libsanitizer/tsan/tsan_flags.cc @@ -24,13 +24,43 @@ Flags *flags() { // Can be overriden in frontend. #ifdef TSAN_EXTERNAL_HOOKS void OverrideFlags(Flags *f); +extern "C" const char* __tsan_default_options(); #else -SANITIZER_INTERFACE_ATTRIBUTE void WEAK OverrideFlags(Flags *f) { (void)f; } +extern "C" const char *WEAK __tsan_default_options() { + return ""; +} #endif +static void ParseFlags(Flags *f, const char *env) { + ParseFlag(env, &f->enable_annotations, "enable_annotations"); + ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); + ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); + ParseFlag(env, &f->suppress_java, "suppress_java"); + ParseFlag(env, &f->report_bugs, "report_bugs"); + ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); + ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); + ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); + ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); + ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); + ParseFlag(env, &f->suppressions, "suppressions"); + ParseFlag(env, &f->print_suppressions, "print_suppressions"); + ParseFlag(env, &f->print_benign, "print_benign"); + ParseFlag(env, &f->exitcode, "exitcode"); + ParseFlag(env, &f->halt_on_error, "halt_on_error"); + ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); + ParseFlag(env, &f->profile_memory, "profile_memory"); + ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); + ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); + ParseFlag(env, &f->memory_limit_mb, "memory_limit_mb"); + ParseFlag(env, &f->stop_on_start, "stop_on_start"); + ParseFlag(env, &f->running_on_valgrind, "running_on_valgrind"); + ParseFlag(env, &f->history_size, "history_size"); + ParseFlag(env, &f->io_sync, "io_sync"); +} + void InitializeFlags(Flags *f, const char *env) { internal_memset(f, 0, sizeof(*f)); @@ -45,57 +75,35 @@ void InitializeFlags(Flags *f, const char *env) { f->report_signal_unsafe = true; f->report_atomic_races = true; f->force_seq_cst_atomics = false; - f->strip_path_prefix = ""; f->suppressions = ""; f->print_suppressions = false; f->print_benign = false; f->exitcode = 66; f->halt_on_error = false; - f->log_path = "stderr"; f->atexit_sleep_ms = 1000; - f->verbosity = 0; f->profile_memory = ""; f->flush_memory_ms = 0; f->flush_symbolizer_ms = 5000; + f->memory_limit_mb = 0; f->stop_on_start = false; f->running_on_valgrind = false; - f->external_symbolizer_path = ""; f->history_size = kGoMode ? 1 : 2; // There are a lot of goroutines in Go. f->io_sync = 1; - f->allocator_may_return_null = false; + + SetCommonFlagsDefaults(f); // Let a frontend override. OverrideFlags(f); - + ParseFlags(f, __tsan_default_options()); + ParseCommonFlagsFromString(f, __tsan_default_options()); // Override from command line. - ParseFlag(env, &f->enable_annotations, "enable_annotations"); - ParseFlag(env, &f->suppress_equal_stacks, "suppress_equal_stacks"); - ParseFlag(env, &f->suppress_equal_addresses, "suppress_equal_addresses"); - ParseFlag(env, &f->suppress_java, "suppress_java"); - ParseFlag(env, &f->report_bugs, "report_bugs"); - ParseFlag(env, &f->report_thread_leaks, "report_thread_leaks"); - ParseFlag(env, &f->report_destroy_locked, "report_destroy_locked"); - ParseFlag(env, &f->report_signal_unsafe, "report_signal_unsafe"); - ParseFlag(env, &f->report_atomic_races, "report_atomic_races"); - ParseFlag(env, &f->force_seq_cst_atomics, "force_seq_cst_atomics"); - ParseFlag(env, &f->strip_path_prefix, "strip_path_prefix"); - ParseFlag(env, &f->suppressions, "suppressions"); - ParseFlag(env, &f->print_suppressions, "print_suppressions"); - ParseFlag(env, &f->print_benign, "print_benign"); - ParseFlag(env, &f->exitcode, "exitcode"); - ParseFlag(env, &f->halt_on_error, "halt_on_error"); - ParseFlag(env, &f->log_path, "log_path"); - ParseFlag(env, &f->atexit_sleep_ms, "atexit_sleep_ms"); - ParseFlag(env, &f->verbosity, "verbosity"); - ParseFlag(env, &f->profile_memory, "profile_memory"); - ParseFlag(env, &f->flush_memory_ms, "flush_memory_ms"); - ParseFlag(env, &f->flush_symbolizer_ms, "flush_symbolizer_ms"); - ParseFlag(env, &f->stop_on_start, "stop_on_start"); - ParseFlag(env, &f->external_symbolizer_path, "external_symbolizer_path"); - ParseFlag(env, &f->history_size, "history_size"); - ParseFlag(env, &f->io_sync, "io_sync"); - ParseFlag(env, &f->allocator_may_return_null, "allocator_may_return_null"); + ParseFlags(f, env); + ParseCommonFlagsFromString(f, env); + + // Copy back to common flags. + *common_flags() = *f; + // Sanity check. if (!f->report_bugs) { f->report_thread_leaks = false; f->report_destroy_locked = false; @@ -113,8 +121,6 @@ void InitializeFlags(Flags *f, const char *env) { " (must be [0..2])\n"); Die(); } - - common_flags()->allocator_may_return_null = f->allocator_may_return_null; } } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_flags.h b/libsanitizer/tsan/tsan_flags.h index a7571c9..05d11a4 100644 --- a/libsanitizer/tsan/tsan_flags.h +++ b/libsanitizer/tsan/tsan_flags.h @@ -18,9 +18,11 @@ // header may be included in the user code, and shouldn't include // other headers from TSan or common sanitizer runtime. +#include "sanitizer_common/sanitizer_flags.h" + namespace __tsan { -struct Flags { +struct Flags : CommonFlags { // Enable dynamic annotations, otherwise they are no-ops. bool enable_annotations; // Supress a race report if we've already output another race report @@ -46,8 +48,6 @@ struct Flags { // If set, all atomics are effectively sequentially consistent (seq_cst), // regardless of what user actually specified. bool force_seq_cst_atomics; - // Strip that prefix from file paths in reports. - const char *strip_path_prefix; // Suppressions filename. const char *suppressions; // Print matched suppressions at exit. @@ -58,27 +58,22 @@ struct Flags { int exitcode; // Exit after first reported error. bool halt_on_error; - // Write logs to "log_path.pid". - // The special values are "stdout" and "stderr". - // The default is "stderr". - const char *log_path; // Sleep in main thread before exiting for that many ms // (useful to catch "at exit" races). int atexit_sleep_ms; - // Verbosity level (0 - silent, 1 - a bit of output, 2+ - more output). - int verbosity; // If set, periodically write memory profile to that file. const char *profile_memory; // Flush shadow memory every X ms. int flush_memory_ms; // Flush symbolizer caches every X ms. int flush_symbolizer_ms; + // Resident memory limit in MB to aim at. + // If the process consumes more memory, then TSan will flush shadow memory. + int memory_limit_mb; // Stops on start until __tsan_resume() is called (for debugging). bool stop_on_start; // Controls whether RunningOnValgrind() returns true or false. bool running_on_valgrind; - // Path to external symbolizer. - const char *external_symbolizer_path; // Per-thread history size, controls how many previous memory accesses // are remembered per thread. Possible values are [0..7]. // history_size=0 amounts to 32K memory accesses. Each next value doubles @@ -90,8 +85,6 @@ struct Flags { // 1 - reasonable level of synchronization (write->read) // 2 - global synchronization of all IO operations int io_sync; - // If false, the allocator will crash instead of returning 0 on out-of-memory. - bool allocator_may_return_null; }; Flags *flags(); diff --git a/libsanitizer/tsan/tsan_ignoreset.cc b/libsanitizer/tsan/tsan_ignoreset.cc new file mode 100644 index 0000000..f0aec42 --- /dev/null +++ b/libsanitizer/tsan/tsan_ignoreset.cc @@ -0,0 +1,45 @@ +//===-- tsan_ignoreset.cc -------------------------------------------------===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +//===----------------------------------------------------------------------===// +#include "tsan_ignoreset.h" + +namespace __tsan { + +const uptr IgnoreSet::kMaxSize; + +IgnoreSet::IgnoreSet() + : size_() { +} + +void IgnoreSet::Add(u32 stack_id) { + if (size_ == kMaxSize) + return; + for (uptr i = 0; i < size_; i++) { + if (stacks_[i] == stack_id) + return; + } + stacks_[size_++] = stack_id; +} + +void IgnoreSet::Reset() { + size_ = 0; +} + +uptr IgnoreSet::Size() const { + return size_; +} + +u32 IgnoreSet::At(uptr i) const { + CHECK_LT(i, size_); + CHECK_LE(size_, kMaxSize); + return stacks_[i]; +} + +} // namespace __tsan diff --git a/libsanitizer/tsan/tsan_ignoreset.h b/libsanitizer/tsan/tsan_ignoreset.h new file mode 100644 index 0000000..5a250b7 --- /dev/null +++ b/libsanitizer/tsan/tsan_ignoreset.h @@ -0,0 +1,36 @@ +//===-- tsan_ignoreset.h ----------------------------------------*- C++ -*-===// +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of ThreadSanitizer (TSan), a race detector. +// +// IgnoreSet holds a set of stack traces where ignores were enabled. +//===----------------------------------------------------------------------===// +#ifndef TSAN_IGNORESET_H +#define TSAN_IGNORESET_H + +#include "tsan_defs.h" + +namespace __tsan { + +class IgnoreSet { + public: + static const uptr kMaxSize = 16; + + IgnoreSet(); + void Add(u32 stack_id); + void Reset(); + uptr Size() const; + u32 At(uptr i) const; + + private: + uptr size_; + u32 stacks_[kMaxSize]; +}; + +} // namespace __tsan + +#endif // TSAN_IGNORESET_H diff --git a/libsanitizer/tsan/tsan_interceptors.cc b/libsanitizer/tsan/tsan_interceptors.cc index eaaf9e3..0574beb 100644 --- a/libsanitizer/tsan/tsan_interceptors.cc +++ b/libsanitizer/tsan/tsan_interceptors.cc @@ -20,6 +20,7 @@ #include "interception/interception.h" #include "tsan_interface.h" #include "tsan_platform.h" +#include "tsan_suppressions.h" #include "tsan_rtl.h" #include "tsan_mman.h" #include "tsan_fd.h" @@ -40,9 +41,8 @@ struct ucontext_t { extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); -extern "C" int pthread_attr_getdetachstate(void *attr, int *v); +DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); -extern "C" int pthread_attr_getstacksize(void *attr, uptr *stacksize); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); extern "C" int pthread_mutexattr_gettype(void *a, int *type); @@ -64,6 +64,7 @@ const int PTHREAD_MUTEX_RECURSIVE = 1; const int PTHREAD_MUTEX_RECURSIVE_NP = 1; const int EINVAL = 22; const int EBUSY = 16; +const int EOWNERDEAD = 130; const int EPOLL_CTL_ADD = 1; const int SIGILL = 4; const int SIGABRT = 6; @@ -124,18 +125,16 @@ struct SignalContext { SignalDesc pending_signals[kSigCount]; }; -// Used to ignore interceptors coming directly from libjvm.so. -atomic_uintptr_t libjvm_begin; -atomic_uintptr_t libjvm_end; +// The object is 64-byte aligned, because we want hot data to be located in +// a single cache line if possible (it's accessed in every interceptor). +static ALIGNED(64) char libignore_placeholder[sizeof(LibIgnore)]; +static LibIgnore *libignore() { + return reinterpret_cast(&libignore_placeholder[0]); +} -static bool libjvm_check(uptr pc) { - uptr begin = atomic_load(&libjvm_begin, memory_order_relaxed); - if (begin != 0 && pc >= begin) { - uptr end = atomic_load(&libjvm_end, memory_order_relaxed); - if (end != 0 && pc < end) - return true; - } - return false; +void InitializeLibIgnore() { + libignore()->Init(*GetSuppressionContext()); + libignore()->OnLibraryLoaded(0); } } // namespace __tsan @@ -159,13 +158,17 @@ class ScopedInterceptor { ~ScopedInterceptor(); private: ThreadState *const thr_; + const uptr pc_; const int in_rtl_; + bool in_ignored_lib_; }; ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, uptr pc) : thr_(thr) - , in_rtl_(thr->in_rtl) { + , pc_(pc) + , in_rtl_(thr->in_rtl) + , in_ignored_lib_(false) { if (thr_->in_rtl == 0) { Initialize(thr); FuncEntry(thr, pc); @@ -174,9 +177,18 @@ ScopedInterceptor::ScopedInterceptor(ThreadState *thr, const char *fname, } else { thr_->in_rtl++; } + if (!thr_->in_ignored_lib && libignore()->IsIgnored(pc)) { + in_ignored_lib_ = true; + thr_->in_ignored_lib = true; + ThreadIgnoreBegin(thr_, pc_); + } } ScopedInterceptor::~ScopedInterceptor() { + if (in_ignored_lib_) { + thr_->in_ignored_lib = false; + ThreadIgnoreEnd(thr_, pc_); + } thr_->in_rtl--; if (thr_->in_rtl == 0) { FuncExit(thr_); @@ -201,7 +213,7 @@ ScopedInterceptor::~ScopedInterceptor() { Printf("FATAL: ThreadSanitizer: failed to intercept %s\n", #func); \ Die(); \ } \ - if (thr->in_rtl > 1 || libjvm_check(pc)) \ + if (thr->in_rtl > 1 || thr->in_ignored_lib) \ return REAL(func)(__VA_ARGS__); \ /**/ @@ -244,6 +256,28 @@ TSAN_INTERCEPTOR(int, nanosleep, void *req, void *rem) { return res; } +TSAN_INTERCEPTOR(void*, dlopen, const char *filename, int flag) { + SCOPED_INTERCEPTOR_RAW(dlopen, filename, flag); + // dlopen will execute global constructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + void *res = REAL(dlopen)(filename, flag); + thr->in_rtl = 1; + libignore()->OnLibraryLoaded(filename); + return res; +} + +TSAN_INTERCEPTOR(int, dlclose, void *handle) { + SCOPED_INTERCEPTOR_RAW(dlclose, handle); + // dlclose will execute global destructors, so it must be not in rtl. + CHECK_EQ(thr->in_rtl, 1); + thr->in_rtl = 0; + int res = REAL(dlclose)(handle); + thr->in_rtl = 1; + libignore()->OnLibraryUnloaded(); + return res; +} + class AtExitContext { public: AtExitContext() @@ -326,9 +360,9 @@ TSAN_INTERCEPTOR(int, __cxa_atexit, void (*f)(void *a), void *arg, void *dso) { if (dso) { // Memory allocation in __cxa_atexit will race with free during exit, // because we do not see synchronization around atexit callback list. - ThreadIgnoreBegin(thr); + ThreadIgnoreBegin(thr, pc); int res = REAL(__cxa_atexit)(f, arg, dso); - ThreadIgnoreEnd(thr); + ThreadIgnoreEnd(thr, pc); return res; } return atexit_ctx->atexit(thr, pc, false, (void(*)())f, arg); @@ -439,7 +473,7 @@ TSAN_INTERCEPTOR(void, siglongjmp, uptr *env, int val) { } TSAN_INTERCEPTOR(void*, malloc, uptr size) { - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_malloc(size); void *p = 0; { @@ -456,7 +490,7 @@ TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { } TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_calloc(size, n); if (__sanitizer::CallocShouldReturnNullDueToOverflow(size, n)) return AllocatorReturnNull(); @@ -472,7 +506,7 @@ TSAN_INTERCEPTOR(void*, calloc, uptr size, uptr n) { } TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_realloc(p, size); if (p) invoke_free_hook(p); @@ -487,7 +521,7 @@ TSAN_INTERCEPTOR(void*, realloc, void *p, uptr size) { TSAN_INTERCEPTOR(void, free, void *p) { if (p == 0) return; - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(free, p); @@ -497,7 +531,7 @@ TSAN_INTERCEPTOR(void, free, void *p) { TSAN_INTERCEPTOR(void, cfree, void *p) { if (p == 0) return; - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) + if (cur_thread()->in_symbolizer) return __libc_free(p); invoke_free_hook(p); SCOPED_INTERCEPTOR_RAW(cfree, p); @@ -506,13 +540,11 @@ TSAN_INTERCEPTOR(void, cfree, void *p) { TSAN_INTERCEPTOR(uptr, malloc_usable_size, void *p) { SCOPED_INTERCEPTOR_RAW(malloc_usable_size, p); - if (libjvm_check(pc)) - return malloc_usable_size(p); return user_alloc_usable_size(thr, pc, p); } #define OPERATOR_NEW_BODY(mangled_name) \ - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \ + if (cur_thread()->in_symbolizer) \ return __libc_malloc(size); \ void *p = 0; \ { \ @@ -548,7 +580,7 @@ void *operator new[](__sanitizer::uptr size, std::nothrow_t const&) { #define OPERATOR_DELETE_BODY(mangled_name) \ if (ptr == 0) return; \ - if (cur_thread()->in_symbolizer || libjvm_check(GET_CALLER_PC())) \ + if (cur_thread()->in_symbolizer) \ return __libc_free(ptr); \ invoke_free_hook(ptr); \ SCOPED_INTERCEPTOR_RAW(mangled_name, ptr); \ @@ -682,15 +714,6 @@ TSAN_INTERCEPTOR(const char*, strstr, const char *s1, const char *s2) { TSAN_INTERCEPTOR(char*, strdup, const char *str) { SCOPED_TSAN_INTERCEPTOR(strdup, str); - if (libjvm_check(pc)) { - // The memory must come from libc malloc, - // and we must not instrument accesses in this case. - uptr n = internal_strlen(str) + 1; - void *p = __libc_malloc(n); - if (p == 0) - return 0; - return (char*)internal_memcpy(p, str, n); - } // strdup will call malloc, so no instrumentation is required here. return REAL(strdup)(str); } @@ -745,23 +768,23 @@ TSAN_INTERCEPTOR(int, munmap, void *addr, long_t sz) { } TSAN_INTERCEPTOR(void*, memalign, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(memalign, align, sz); + SCOPED_INTERCEPTOR_RAW(memalign, align, sz); return user_alloc(thr, pc, sz, align); } TSAN_INTERCEPTOR(void*, valloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(valloc, sz); + SCOPED_INTERCEPTOR_RAW(valloc, sz); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(void*, pvalloc, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(pvalloc, sz); + SCOPED_INTERCEPTOR_RAW(pvalloc, sz); sz = RoundUp(sz, GetPageSizeCached()); return user_alloc(thr, pc, sz, GetPageSizeCached()); } TSAN_INTERCEPTOR(int, posix_memalign, void **memptr, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(posix_memalign, memptr, align, sz); + SCOPED_INTERCEPTOR_RAW(posix_memalign, memptr, align, sz); *memptr = user_alloc(thr, pc, sz, align); return 0; } @@ -830,7 +853,8 @@ extern "C" void *__tsan_thread_start_func(void *arg) { { ThreadState *thr = cur_thread(); ScopedInRtl in_rtl; - if (pthread_setspecific(g_thread_finalize_key, (void*)4)) { + if (pthread_setspecific(g_thread_finalize_key, + (void *)kPthreadDestructorIterations)) { Printf("ThreadSanitizer: failed to set thread key\n"); Die(); } @@ -850,21 +874,15 @@ extern "C" void *__tsan_thread_start_func(void *arg) { TSAN_INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void*), void * param) { - SCOPED_TSAN_INTERCEPTOR(pthread_create, th, attr, callback, param); + SCOPED_INTERCEPTOR_RAW(pthread_create, th, attr, callback, param); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); attr = &myattr; } int detached = 0; - pthread_attr_getdetachstate(attr, &detached); - -#if defined(TSAN_DEBUG_OUTPUT) - int verbosity = (TSAN_DEBUG_OUTPUT); -#else - int verbosity = 0; -#endif - AdjustStackSizeLinux(attr, verbosity); + REAL(pthread_attr_getdetachstate)(attr, &detached); + AdjustStackSizeLinux(attr); ThreadParam p; p.callback = callback; @@ -884,7 +902,7 @@ TSAN_INTERCEPTOR(int, pthread_create, } TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) { - SCOPED_TSAN_INTERCEPTOR(pthread_join, th, ret); + SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret); int tid = ThreadTid(thr, pc, (uptr)th); int res = BLOCK_REAL(pthread_join)(th, ret); if (res == 0) { @@ -928,21 +946,13 @@ TSAN_INTERCEPTOR(int, pthread_mutex_destroy, void *m) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_lock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_lock, m); - int res = REAL(pthread_mutex_lock)(m); - if (res == 0) { - MutexLock(thr, pc, (uptr)m); - } - return res; -} - TSAN_INTERCEPTOR(int, pthread_mutex_trylock, void *m) { SCOPED_TSAN_INTERCEPTOR(pthread_mutex_trylock, m); int res = REAL(pthread_mutex_trylock)(m); - if (res == 0) { + if (res == EOWNERDEAD) + MutexRepair(thr, pc, (uptr)m); + if (res == 0 || res == EOWNERDEAD) MutexLock(thr, pc, (uptr)m); - } return res; } @@ -955,13 +965,6 @@ TSAN_INTERCEPTOR(int, pthread_mutex_timedlock, void *m, void *abstime) { return res; } -TSAN_INTERCEPTOR(int, pthread_mutex_unlock, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_mutex_unlock, m); - MutexUnlock(thr, pc, (uptr)m); - int res = REAL(pthread_mutex_unlock)(m); - return res; -} - TSAN_INTERCEPTOR(int, pthread_spin_init, void *m, int pshared) { SCOPED_TSAN_INTERCEPTOR(pthread_spin_init, m, pshared); int res = REAL(pthread_spin_init)(m, pshared); @@ -1084,13 +1087,6 @@ TSAN_INTERCEPTOR(int, pthread_rwlock_unlock, void *m) { return res; } -TSAN_INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_init, c, a); - MemoryWrite(thr, pc, (uptr)c, kSizeLog1); - int res = REAL(pthread_cond_init)(c, a); - return res; -} - TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_destroy, c); MemoryWrite(thr, pc, (uptr)c, kSizeLog1); @@ -1098,29 +1094,6 @@ TSAN_INTERCEPTOR(int, pthread_cond_destroy, void *c) { return res; } -TSAN_INTERCEPTOR(int, pthread_cond_signal, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_signal, c); - MemoryRead(thr, pc, (uptr)c, kSizeLog1); - int res = REAL(pthread_cond_signal)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_broadcast, void *c) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_broadcast, c); - MemoryRead(thr, pc, (uptr)c, kSizeLog1); - int res = REAL(pthread_cond_broadcast)(c); - return res; -} - -TSAN_INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { - SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, c, m); - MutexUnlock(thr, pc, (uptr)m); - MemoryRead(thr, pc, (uptr)c, kSizeLog1); - int res = REAL(pthread_cond_wait)(c, m); - MutexLock(thr, pc, (uptr)m); - return res; -} - TSAN_INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, c, m, abstime); @@ -1158,7 +1131,9 @@ TSAN_INTERCEPTOR(int, pthread_barrier_wait, void *b) { } TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { - SCOPED_TSAN_INTERCEPTOR(pthread_once, o, f); + SCOPED_INTERCEPTOR_RAW(pthread_once, o, f); + // Using SCOPED_INTERCEPTOR_RAW, because if we are called from an ignored lib, + // the user callback must be executed with thr->in_rtl == 0. if (o == 0 || f == 0) return EINVAL; atomic_uint32_t *a = static_cast(o); @@ -1170,14 +1145,16 @@ TSAN_INTERCEPTOR(int, pthread_once, void *o, void (*f)()) { (*f)(); CHECK_EQ(thr->in_rtl, 0); thr->in_rtl = old_in_rtl; - Release(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Release(thr, pc, (uptr)o); atomic_store(a, 2, memory_order_release); } else { while (v != 2) { pthread_yield(); v = atomic_load(a, memory_order_acquire); } - Acquire(thr, pc, (uptr)o); + if (!thr->in_ignored_lib) + Acquire(thr, pc, (uptr)o); } return 0; } @@ -1496,22 +1473,28 @@ TSAN_INTERCEPTOR(int, pipe2, int *pipefd, int flags) { TSAN_INTERCEPTOR(long_t, send, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(send, fd, buf, len, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(send)(fd, buf, len, flags); return res; } TSAN_INTERCEPTOR(long_t, sendmsg, int fd, void *msg, int flags) { SCOPED_TSAN_INTERCEPTOR(sendmsg, fd, msg, flags); - if (fd >= 0) + if (fd >= 0) { + FdAccess(thr, pc, fd); FdRelease(thr, pc, fd); + } int res = REAL(sendmsg)(fd, msg, flags); return res; } TSAN_INTERCEPTOR(long_t, recv, int fd, void *buf, long_t len, int flags) { SCOPED_TSAN_INTERCEPTOR(recv, fd, buf, len, flags); + if (fd >= 0) + FdAccess(thr, pc, fd); int res = REAL(recv)(fd, buf, len, flags); if (res >= 0 && fd >= 0) { FdAcquire(thr, pc, fd); @@ -1556,6 +1539,7 @@ TSAN_INTERCEPTOR(void*, freopen, char *path, char *mode, void *stream) { } TSAN_INTERCEPTOR(int, fclose, void *stream) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fclose, stream); if (stream) { @@ -1568,6 +1552,7 @@ TSAN_INTERCEPTOR(int, fclose, void *stream) { } TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fread, ptr, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)ptr, size * nmemb, true); @@ -1576,6 +1561,7 @@ TSAN_INTERCEPTOR(uptr, fread, void *ptr, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { + // libc file streams can call user-supplied functions, see fopencookie. { SCOPED_TSAN_INTERCEPTOR(fwrite, p, size, nmemb, f); MemoryAccessRange(thr, pc, (uptr)p, size * nmemb, false); @@ -1584,7 +1570,10 @@ TSAN_INTERCEPTOR(uptr, fwrite, const void *p, uptr size, uptr nmemb, void *f) { } TSAN_INTERCEPTOR(int, fflush, void *stream) { - SCOPED_TSAN_INTERCEPTOR(fflush, stream); + // libc file streams can call user-supplied functions, see fopencookie. + { + SCOPED_TSAN_INTERCEPTOR(fflush, stream); + } return REAL(fflush)(stream); } @@ -1617,21 +1606,23 @@ TSAN_INTERCEPTOR(void*, opendir, char *path) { TSAN_INTERCEPTOR(int, epoll_ctl, int epfd, int op, int fd, void *ev) { SCOPED_TSAN_INTERCEPTOR(epoll_ctl, epfd, op, fd, ev); - if (op == EPOLL_CTL_ADD && epfd >= 0) { + if (epfd >= 0) + FdAccess(thr, pc, epfd); + if (epfd >= 0 && fd >= 0) + FdAccess(thr, pc, fd); + if (op == EPOLL_CTL_ADD && epfd >= 0) FdRelease(thr, pc, epfd); - } int res = REAL(epoll_ctl)(epfd, op, fd, ev); - if (fd >= 0) - FdAccess(thr, pc, fd); return res; } 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) + FdAccess(thr, pc, epfd); int res = BLOCK_REAL(epoll_wait)(epfd, ev, cnt, timeout); - if (res > 0 && epfd >= 0) { + if (res > 0 && epfd >= 0) FdAcquire(thr, pc, epfd); - } return res; } @@ -1777,13 +1768,13 @@ TSAN_INTERCEPTOR(int, getaddrinfo, void *node, void *service, // We miss atomic synchronization in getaddrinfo, // and can report false race between malloc and free // inside of getaddrinfo. So ignore memory accesses. - ThreadIgnoreBegin(thr); + ThreadIgnoreBegin(thr, pc); // getaddrinfo calls fopen, which can be intercepted by user. thr->in_rtl--; CHECK_EQ(thr->in_rtl, 0); int res = REAL(getaddrinfo)(node, service, hints, rv); thr->in_rtl++; - ThreadIgnoreEnd(thr); + ThreadIgnoreEnd(thr, pc); return res; } @@ -1818,7 +1809,7 @@ TSAN_INTERCEPTOR(int, munlockall, void) { } TSAN_INTERCEPTOR(int, fork, int fake) { - SCOPED_TSAN_INTERCEPTOR(fork, fake); + SCOPED_INTERCEPTOR_RAW(fork, fake); int pid = REAL(fork)(fake); if (pid == 0) { // child @@ -1829,12 +1820,26 @@ TSAN_INTERCEPTOR(int, fork, int fake) { return pid; } +static int OnExit(ThreadState *thr) { + int status = Finalize(thr); + REAL(fflush)(0); + return status; +} + struct TsanInterceptorContext { ThreadState *thr; const uptr caller_pc; const uptr pc; }; +static void HandleRecvmsg(ThreadState *thr, uptr pc, + __sanitizer_msghdr *msg) { + int fds[64]; + int cnt = ExtractRecvmsgFDs(msg, fds, ARRAY_SIZE(fds)); + for (int i = 0; i < cnt; i++) + FdEventCreate(thr, pc, fds[i]); +} + #include "sanitizer_common/sanitizer_platform_interceptors.h" // Causes interceptor recursion (getpwuid_r() calls fopen()) #undef SANITIZER_INTERCEPT_GETPWNAM_AND_FRIENDS @@ -1845,31 +1850,66 @@ struct TsanInterceptorContext { // Causes interceptor recursion (glob64() calls lstat64()) #undef SANITIZER_INTERCEPT_GLOB +#define COMMON_INTERCEPT_FUNCTION(name) INTERCEPT_FUNCTION(name) #define COMMON_INTERCEPTOR_UNPOISON_PARAM(ctx, count) \ do { \ } while (false) + #define COMMON_INTERCEPTOR_WRITE_RANGE(ctx, ptr, size) \ MemoryAccessRange(((TsanInterceptorContext *)ctx)->thr, \ ((TsanInterceptorContext *)ctx)->pc, (uptr)ptr, size, \ true) + #define COMMON_INTERCEPTOR_READ_RANGE(ctx, ptr, size) \ MemoryAccessRange(((TsanInterceptorContext *) ctx)->thr, \ ((TsanInterceptorContext *) ctx)->pc, (uptr) ptr, size, \ false) + #define COMMON_INTERCEPTOR_ENTER(ctx, func, ...) \ SCOPED_TSAN_INTERCEPTOR(func, __VA_ARGS__); \ TsanInterceptorContext _ctx = {thr, caller_pc, pc}; \ ctx = (void *)&_ctx; \ (void) ctx; + #define COMMON_INTERCEPTOR_FD_ACQUIRE(ctx, fd) \ FdAcquire(((TsanInterceptorContext *) ctx)->thr, pc, fd) + #define COMMON_INTERCEPTOR_FD_RELEASE(ctx, fd) \ FdRelease(((TsanInterceptorContext *) ctx)->thr, pc, fd) + +#define COMMON_INTERCEPTOR_FD_ACCESS(ctx, fd) \ + FdAccess(((TsanInterceptorContext *) ctx)->thr, pc, fd) + #define COMMON_INTERCEPTOR_FD_SOCKET_ACCEPT(ctx, fd, newfd) \ FdSocketAccept(((TsanInterceptorContext *) ctx)->thr, pc, fd, newfd) + #define COMMON_INTERCEPTOR_SET_THREAD_NAME(ctx, name) \ ThreadSetName(((TsanInterceptorContext *) ctx)->thr, name) + +#define COMMON_INTERCEPTOR_SET_PTHREAD_NAME(ctx, thread, name) \ + CTX()->thread_registry->SetThreadNameByUserId(thread, name) + #define COMMON_INTERCEPTOR_BLOCK_REAL(name) BLOCK_REAL(name) + +#define COMMON_INTERCEPTOR_ON_EXIT(ctx) \ + OnExit(((TsanInterceptorContext *) ctx)->thr) + +#define COMMON_INTERCEPTOR_MUTEX_LOCK(ctx, m) \ + MutexLock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_UNLOCK(ctx, m) \ + MutexUnlock(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_MUTEX_REPAIR(ctx, m) \ + MutexRepair(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, (uptr)m) + +#define COMMON_INTERCEPTOR_HANDLE_RECVMSG(ctx, msg) \ + HandleRecvmsg(((TsanInterceptorContext *)ctx)->thr, \ + ((TsanInterceptorContext *)ctx)->pc, msg) + #include "sanitizer_common/sanitizer_common_interceptors.inc" #define TSAN_SYSCALL() \ @@ -1899,10 +1939,33 @@ static void syscall_access_range(uptr pc, uptr p, uptr s, bool write) { MemoryAccessRange(thr, pc, p, s, write); } +static void syscall_acquire(uptr pc, uptr addr) { + TSAN_SYSCALL(); + Acquire(thr, pc, addr); + Printf("syscall_acquire(%p)\n", addr); +} + +static void syscall_release(uptr pc, uptr addr) { + TSAN_SYSCALL(); + Printf("syscall_release(%p)\n", addr); + Release(thr, pc, addr); +} + static void syscall_fd_close(uptr pc, int fd) { TSAN_SYSCALL(); - if (fd >= 0) - FdClose(thr, pc, fd); + FdClose(thr, pc, fd); +} + +static USED void syscall_fd_acquire(uptr pc, int fd) { + TSAN_SYSCALL(); + FdAcquire(thr, pc, fd); + Printf("syscall_fd_acquire(%p)\n", fd); +} + +static USED void syscall_fd_release(uptr pc, int fd) { + TSAN_SYSCALL(); + Printf("syscall_fd_release(%p)\n", fd); + FdRelease(thr, pc, fd); } static void syscall_pre_fork(uptr pc) { @@ -1921,22 +1984,54 @@ static void syscall_post_fork(uptr pc, int res) { #define COMMON_SYSCALL_PRE_READ_RANGE(p, s) \ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), false) + #define COMMON_SYSCALL_PRE_WRITE_RANGE(p, s) \ syscall_access_range(GET_CALLER_PC(), (uptr)(p), (uptr)(s), true) + #define COMMON_SYSCALL_POST_READ_RANGE(p, s) \ - do { } while (false) + do { \ + (void)(p); \ + (void)(s); \ + } while (false) + #define COMMON_SYSCALL_POST_WRITE_RANGE(p, s) \ - do { } while (false) -#define COMMON_SYSCALL_FD_CLOSE(fd) \ - syscall_fd_close(GET_CALLER_PC(), fd) + do { \ + (void)(p); \ + (void)(s); \ + } while (false) + +#define COMMON_SYSCALL_ACQUIRE(addr) \ + syscall_acquire(GET_CALLER_PC(), (uptr)(addr)) + +#define COMMON_SYSCALL_RELEASE(addr) \ + syscall_release(GET_CALLER_PC(), (uptr)(addr)) + +#define COMMON_SYSCALL_FD_CLOSE(fd) syscall_fd_close(GET_CALLER_PC(), fd) + +#define COMMON_SYSCALL_FD_ACQUIRE(fd) syscall_fd_acquire(GET_CALLER_PC(), fd) + +#define COMMON_SYSCALL_FD_RELEASE(fd) syscall_fd_release(GET_CALLER_PC(), fd) + #define COMMON_SYSCALL_PRE_FORK() \ syscall_pre_fork(GET_CALLER_PC()) + #define COMMON_SYSCALL_POST_FORK(res) \ syscall_post_fork(GET_CALLER_PC(), res) + #include "sanitizer_common/sanitizer_common_syscalls.inc" namespace __tsan { +static void finalize(void *arg) { + ThreadState *thr = cur_thread(); + uptr pc = 0; + atexit_ctx->exit(thr, pc); + int status = Finalize(thr); + REAL(fflush)(0); + if (status) + REAL(_exit)(status); +} + void ProcessPendingSignals(ThreadState *thr) { CHECK_EQ(thr->in_rtl, 0); SignalContext *sctx = SigCtx(thr); @@ -1986,16 +2081,6 @@ void ProcessPendingSignals(ThreadState *thr) { thr->in_signal_handler = false; } -static void finalize(void *arg) { - ThreadState * thr = cur_thread(); - uptr pc = 0; - atexit_ctx->exit(thr, pc); - int status = Finalize(cur_thread()); - REAL(fflush)(0); - if (status) - _exit(status); -} - static void unreachable() { Printf("FATAL: ThreadSanitizer: unreachable called\n"); Die(); @@ -2015,10 +2100,14 @@ void InitializeInterceptors() { SANITIZER_COMMON_INTERCEPTORS_INIT; - TSAN_INTERCEPT(setjmp); - TSAN_INTERCEPT(_setjmp); - TSAN_INTERCEPT(sigsetjmp); - TSAN_INTERCEPT(__sigsetjmp); + // We can not use TSAN_INTERCEPT to get setjmp addr, + // because it does &setjmp and setjmp is not present in some versions of libc. + using __interception::GetRealFunctionAddress; + GetRealFunctionAddress("setjmp", (uptr*)&REAL(setjmp), 0, 0); + GetRealFunctionAddress("_setjmp", (uptr*)&REAL(_setjmp), 0, 0); + GetRealFunctionAddress("sigsetjmp", (uptr*)&REAL(sigsetjmp), 0, 0); + GetRealFunctionAddress("__sigsetjmp", (uptr*)&REAL(__sigsetjmp), 0, 0); + TSAN_INTERCEPT(longjmp); TSAN_INTERCEPT(siglongjmp); @@ -2057,10 +2146,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); - TSAN_INTERCEPT(pthread_mutex_lock); TSAN_INTERCEPT(pthread_mutex_trylock); TSAN_INTERCEPT(pthread_mutex_timedlock); - TSAN_INTERCEPT(pthread_mutex_unlock); TSAN_INTERCEPT(pthread_spin_init); TSAN_INTERCEPT(pthread_spin_destroy); @@ -2078,12 +2165,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT(pthread_rwlock_timedwrlock); TSAN_INTERCEPT(pthread_rwlock_unlock); - INTERCEPT_FUNCTION_VER(pthread_cond_init, GLIBC_2.3.2); - INTERCEPT_FUNCTION_VER(pthread_cond_destroy, GLIBC_2.3.2); - INTERCEPT_FUNCTION_VER(pthread_cond_signal, GLIBC_2.3.2); - INTERCEPT_FUNCTION_VER(pthread_cond_broadcast, GLIBC_2.3.2); - INTERCEPT_FUNCTION_VER(pthread_cond_wait, GLIBC_2.3.2); - INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, GLIBC_2.3.2); + INTERCEPT_FUNCTION_VER(pthread_cond_destroy, "GLIBC_2.3.2"); + INTERCEPT_FUNCTION_VER(pthread_cond_timedwait, "GLIBC_2.3.2"); TSAN_INTERCEPT(pthread_barrier_init); TSAN_INTERCEPT(pthread_barrier_destroy); @@ -2172,8 +2255,11 @@ void InitializeInterceptors() { TSAN_INTERCEPT(munlockall); TSAN_INTERCEPT(fork); + TSAN_INTERCEPT(dlopen); + TSAN_INTERCEPT(dlclose); TSAN_INTERCEPT(on_exit); TSAN_INTERCEPT(__cxa_atexit); + TSAN_INTERCEPT(_exit); // 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. @@ -2195,9 +2281,15 @@ void InitializeInterceptors() { } void internal_start_thread(void(*func)(void *arg), void *arg) { + // Start the thread with signals blocked, otherwise it can steal users + // signals. + __sanitizer_kernel_sigset_t set, old; + internal_sigfillset(&set); + internal_sigprocmask(SIG_SETMASK, &set, &old); void *th; REAL(pthread_create)(&th, 0, (void*(*)(void *arg))func, arg); REAL(pthread_detach)(th); + internal_sigprocmask(SIG_SETMASK, &old, 0); } } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_interface_ann.cc b/libsanitizer/tsan/tsan_interface_ann.cc index 46d9e3e..38224f4 100644 --- a/libsanitizer/tsan/tsan_interface_ann.cc +++ b/libsanitizer/tsan/tsan_interface_ann.cc @@ -53,11 +53,11 @@ class ScopedAnnotation { if (!flags()->enable_annotations) \ return; \ ThreadState *thr = cur_thread(); \ - const uptr pc = (uptr)__builtin_return_address(0); \ + const uptr caller_pc = (uptr)__builtin_return_address(0); \ StatInc(thr, StatAnnotation); \ StatInc(thr, Stat##typ); \ - ScopedAnnotation sa(thr, __FUNCTION__, f, l, \ - (uptr)__builtin_return_address(0)); \ + ScopedAnnotation sa(thr, __FUNCTION__, f, l, caller_pc); \ + const uptr pc = __sanitizer::StackTrace::GetCurrentPc(); \ (void)pc; \ /**/ @@ -381,22 +381,32 @@ void INTERFACE_ATTRIBUTE AnnotateBenignRace( void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsBegin); - ThreadIgnoreBegin(thr); + ThreadIgnoreBegin(thr, pc); } void INTERFACE_ATTRIBUTE AnnotateIgnoreReadsEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreReadsEnd); - ThreadIgnoreEnd(thr); + ThreadIgnoreEnd(thr, pc); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesBegin(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesBegin); - ThreadIgnoreBegin(thr); + ThreadIgnoreBegin(thr, pc); } void INTERFACE_ATTRIBUTE AnnotateIgnoreWritesEnd(char *f, int l) { SCOPED_ANNOTATION(AnnotateIgnoreWritesEnd); - ThreadIgnoreEnd(thr); + ThreadIgnoreEnd(thr, pc); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncBegin(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncBegin); + ThreadIgnoreSyncBegin(thr, pc); +} + +void INTERFACE_ATTRIBUTE AnnotateIgnoreSyncEnd(char *f, int l) { + SCOPED_ANNOTATION(AnnotateIgnoreSyncEnd); + ThreadIgnoreSyncEnd(thr, pc); } void INTERFACE_ATTRIBUTE AnnotatePublishMemoryRange( @@ -429,7 +439,7 @@ void INTERFACE_ATTRIBUTE WTFAnnotateHappensAfter(char *f, int l, uptr addr) { void INTERFACE_ATTRIBUTE WTFAnnotateBenignRaceSized( char *f, int l, uptr mem, uptr sz, char *desc) { SCOPED_ANNOTATION(AnnotateBenignRaceSized); - BenignRaceImpl(f, l, mem, 1, desc); + BenignRaceImpl(f, l, mem, sz, desc); } int INTERFACE_ATTRIBUTE RunningOnValgrind() { diff --git a/libsanitizer/tsan/tsan_interface_atomic.cc b/libsanitizer/tsan/tsan_interface_atomic.cc index 02ebb47..180d87b 100644 --- a/libsanitizer/tsan/tsan_interface_atomic.cc +++ b/libsanitizer/tsan/tsan_interface_atomic.cc @@ -249,11 +249,10 @@ static T AtomicLoad(ThreadState *thr, uptr pc, const volatile T *a, // Assume the access is atomic. if (!IsAcquireOrder(mo) && sizeof(T) <= sizeof(a)) { MemoryReadAtomic(thr, pc, (uptr)a, SizeLog()); - return *a; + return *a; // as if atomic } SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); T v = *a; s->mtx.ReadUnlock(); __sync_synchronize(); @@ -271,13 +270,15 @@ static void AtomicStore(ThreadState *thr, uptr pc, volatile T *a, T v, // Strictly saying even relaxed store cuts off release sequence, // so must reset the clock. if (!IsReleaseOrder(mo) && sizeof(T) <= sizeof(a)) { - *a = v; + *a = v; // as if atomic return; } __sync_synchronize(); SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); *a = v; s->mtx.Unlock(); // Trainling memory barrier to provide sequential consistency @@ -291,13 +292,15 @@ static T AtomicRMW(ThreadState *thr, uptr pc, volatile T *a, T v, morder mo) { SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } v = F(a, v); if (s) @@ -355,13 +358,15 @@ static bool AtomicCAS(ThreadState *thr, uptr pc, SyncVar *s = 0; if (mo != mo_relaxed) { s = CTX()->synctab.GetOrCreateAndLock(thr, pc, (uptr)a, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); if (IsAcqRelOrder(mo)) - thr->clock.acq_rel(&s->clock); + AcquireReleaseImpl(thr, pc, &s->clock); else if (IsReleaseOrder(mo)) - thr->clock.release(&s->clock); + ReleaseImpl(thr, pc, &s->clock); else if (IsAcquireOrder(mo)) - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); } T cc = *c; T pr = func_cas(a, cc, v); diff --git a/libsanitizer/tsan/tsan_interface_java.cc b/libsanitizer/tsan/tsan_interface_java.cc index 7cc7257..70b5e5f 100644 --- a/libsanitizer/tsan/tsan_interface_java.cc +++ b/libsanitizer/tsan/tsan_interface_java.cc @@ -94,8 +94,6 @@ class ScopedJavaFunc { static u64 jctx_buf[sizeof(JavaContext) / sizeof(u64) + 1]; static JavaContext *jctx; -extern atomic_uintptr_t libjvm_begin; -extern atomic_uintptr_t libjvm_end; static BlockDesc *getblock(uptr addr) { uptr i = (addr - jctx->heap_begin) / kHeapAlignment; @@ -164,17 +162,6 @@ SyncVar* GetAndRemoveJavaSync(ThreadState *thr, uptr pc, uptr addr) { ScopedJavaFunc scoped(thr, caller_pc); \ /**/ -void __tsan_java_preinit(const char *libjvm_path) { - SCOPED_JAVA_FUNC(__tsan_java_preinit); - if (libjvm_path) { - uptr begin, end; - if (GetCodeRangeForFile(libjvm_path, &begin, &end)) { - atomic_store(&libjvm_begin, begin, memory_order_relaxed); - atomic_store(&libjvm_end, end, memory_order_relaxed); - } - } -} - void __tsan_java_init(jptr heap_begin, jptr heap_size) { SCOPED_JAVA_FUNC(__tsan_java_init); DPrintf("#%d: java_init(%p, %p)\n", thr->tid, heap_begin, heap_size); diff --git a/libsanitizer/tsan/tsan_interface_java.h b/libsanitizer/tsan/tsan_interface_java.h index 818c07b..885ff28 100644 --- a/libsanitizer/tsan/tsan_interface_java.h +++ b/libsanitizer/tsan/tsan_interface_java.h @@ -32,11 +32,7 @@ extern "C" { typedef unsigned long jptr; // NOLINT -// Must be called before any other callback from Java, right after dlopen -// of JVM shared lib. If libjvm_path is specified, then all interceptors -// coming directly from JVM will be ignored. -void __tsan_java_preinit(const char *libjvm_path) INTERFACE_ATTRIBUTE; -// Must be called after __tsan_java_preinit but before any other callback. +// Must be called before any other callback from Java. void __tsan_java_init(jptr heap_begin, jptr heap_size) INTERFACE_ATTRIBUTE; // Must be called when the application exits. // Not necessary the last callback (concurrently running threads are OK). diff --git a/libsanitizer/tsan/tsan_mutexset.h b/libsanitizer/tsan/tsan_mutexset.h index ef60bd4..df36b46 100644 --- a/libsanitizer/tsan/tsan_mutexset.h +++ b/libsanitizer/tsan/tsan_mutexset.h @@ -60,4 +60,4 @@ MutexSet::Desc MutexSet::Get(uptr i) const { return Desc(); } } // namespace __tsan -#endif // TSAN_REPORT_H +#endif // TSAN_MUTEXSET_H diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index ac36c5a..164ee45 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -132,17 +132,24 @@ static inline uptr AlternativeAddress(uptr addr) { void FlushShadowMemory(); void WriteMemoryProfile(char *buf, uptr buf_size); +uptr GetRSS(); const char *InitializePlatform(); void FinalizePlatform(); + +// The additional page is to catch shadow stack overflow as paging fault. +const uptr kTotalTraceSize = (kTraceSize * sizeof(Event) + sizeof(Trace) + 4096 + + 4095) & ~4095; + uptr ALWAYS_INLINE GetThreadTrace(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize; DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } uptr ALWAYS_INLINE GetThreadTraceHeader(int tid) { - uptr p = kTraceMemBegin + (uptr)(tid * 2 + 1) * kTraceSize * sizeof(Event); + uptr p = kTraceMemBegin + (uptr)tid * kTotalTraceSize + + kTraceSize * sizeof(Event); DCHECK_LT(p, kTraceMemBegin + kTraceMemSize); return p; } @@ -153,6 +160,7 @@ void internal_start_thread(void(*func)(void*), void *arg); // Guesses with high probability, may yield both false positives and negatives. bool IsGlobalVar(uptr addr); int ExtractResolvFDs(void *state, int *fds, int nfd); +int ExtractRecvmsgFDs(void *msg, int *fds, int nfd); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_platform_linux.cc b/libsanitizer/tsan/tsan_platform_linux.cc index f13e3c8..fe69430 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cc +++ b/libsanitizer/tsan/tsan_platform_linux.cc @@ -17,6 +17,7 @@ #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_procmaps.h" +#include "sanitizer_common/sanitizer_stoptheworld.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" @@ -31,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -43,6 +45,14 @@ #include #include +#ifdef sa_handler +# undef sa_handler +#endif + +#ifdef sa_sigaction +# undef sa_sigaction +#endif + extern "C" struct mallinfo __libc_mallinfo(); namespace __tsan { @@ -104,10 +114,23 @@ void WriteMemoryProfile(char *buf, uptr buf_size) { mi.arena >> 20, mi.hblkhd >> 20, mi.fordblks >> 20, mi.keepcost >> 20); } -void FlushShadowMemory() { +uptr GetRSS() { + uptr mem[7] = {}; + __sanitizer::GetMemoryProfile(FillProfileCallback, mem, 7); + return mem[6]; +} + + +void FlushShadowMemoryCallback( + const SuspendedThreadsList &suspended_threads_list, + void *argument) { FlushUnneededShadowMemory(kLinuxShadowBeg, kLinuxShadowEnd - kLinuxShadowBeg); } +void FlushShadowMemory() { + StopTheWorld(FlushShadowMemoryCallback, 0); +} + #ifndef TSAN_GO static void ProtectRange(uptr beg, uptr end) { ScopedInRtl in_rtl; @@ -323,6 +346,9 @@ bool IsGlobalVar(uptr addr) { } #ifndef TSAN_GO +// Extract file descriptors passed to glibc internal __res_iclose function. +// This is required to properly "close" the fds, because we do not see internal +// closes within glibc. The code is a pure hack. int ExtractResolvFDs(void *state, int *fds, int nfd) { int cnt = 0; __res_state *statp = (__res_state*)state; @@ -332,6 +358,26 @@ int ExtractResolvFDs(void *state, int *fds, int nfd) { } return cnt; } + +// Extract file descriptors passed via UNIX domain sockets. +// This is requried to properly handle "open" of these fds. +// see 'man recvmsg' and 'man 3 cmsg'. +int ExtractRecvmsgFDs(void *msgp, int *fds, int nfd) { + int res = 0; + msghdr *msg = (msghdr*)msgp; + struct cmsghdr *cmsg = CMSG_FIRSTHDR(msg); + for (; cmsg; cmsg = CMSG_NXTHDR(msg, cmsg)) { + if (cmsg->cmsg_level != SOL_SOCKET || cmsg->cmsg_type != SCM_RIGHTS) + continue; + int n = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(fds[0]); + for (int i = 0; i < n; i++) { + fds[res++] = ((int*)CMSG_DATA(cmsg))[i]; + if (res == nfd) + return res; + } + } + return res; +} #endif diff --git a/libsanitizer/tsan/tsan_report.cc b/libsanitizer/tsan/tsan_report.cc index 15ab22b..f248416 100644 --- a/libsanitizer/tsan/tsan_report.cc +++ b/libsanitizer/tsan/tsan_report.cc @@ -132,7 +132,7 @@ static void PrintLocation(const ReportLocation *loc) { bool print_stack = false; Printf("%s", d.Location()); if (loc->type == ReportLocationGlobal) { - Printf(" Location is global '%s' of size %zu at %zx (%s+%p)\n\n", + Printf(" Location is global '%s' of size %zu at %p (%s+%p)\n\n", loc->name, loc->size, loc->addr, loc->module, loc->offset); } else if (loc->type == ReportLocationHeap) { char thrbuf[kThreadBufSize]; diff --git a/libsanitizer/tsan/tsan_rtl.cc b/libsanitizer/tsan/tsan_rtl.cc index 7f18064..a0f3267 100644 --- a/libsanitizer/tsan/tsan_rtl.cc +++ b/libsanitizer/tsan/tsan_rtl.cc @@ -37,9 +37,13 @@ THREADLOCAL char cur_thread_placeholder[sizeof(ThreadState)] ALIGNED(64); static char ctx_placeholder[sizeof(Context)] ALIGNED(64); // Can be overriden by a front-end. -bool CPP_WEAK OnFinalize(bool failed) { +#ifdef TSAN_EXTERNAL_HOOKS +bool OnFinalize(bool failed); +#else +bool WEAK OnFinalize(bool failed) { return failed; } +#endif static Context *ctx; Context *CTX() { @@ -84,7 +88,6 @@ ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, // they may be accessed before the ctor. // , ignore_reads_and_writes() // , in_rtl() - , shadow_stack_pos(&shadow_stack[0]) #ifndef TSAN_GO , jmp_bufs(MBlockJmpBuf) #endif @@ -128,17 +131,38 @@ static void BackgroundThread(void *arg) { } u64 last_flush = NanoTime(); + uptr last_rss = 0; for (int i = 0; ; i++) { SleepForSeconds(1); u64 now = NanoTime(); // Flush memory if requested. - if (flags()->flush_memory_ms) { + if (flags()->flush_memory_ms > 0) { if (last_flush + flags()->flush_memory_ms * kMs2Ns < now) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: periodic memory flush\n"); FlushShadowMemory(); last_flush = NanoTime(); } } + if (flags()->memory_limit_mb > 0) { + uptr rss = GetRSS(); + uptr limit = uptr(flags()->memory_limit_mb) << 20; + if (flags()->verbosity > 0) { + Printf("ThreadSanitizer: memory flush check" + " RSS=%llu LAST=%llu LIMIT=%llu\n", + (u64)rss>>20, (u64)last_rss>>20, (u64)limit>>20); + } + if (2 * rss > limit + last_rss) { + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: flushing memory due to RSS\n"); + FlushShadowMemory(); + rss = GetRSS(); + if (flags()->verbosity > 0) + Printf("ThreadSanitizer: memory flushed RSS=%llu\n", (u64)rss>>20); + } + last_rss = rss; + } // Write memory profile if requested. if (mprof_fd != kInvalidFd) @@ -174,8 +198,10 @@ 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); - if (addr != (uptr)MmapFixedNoReserve(addr, size)) { - Printf("FATAL: ThreadSanitizer can not mmap thread trace\n"); + uptr addr1 = (uptr)MmapFixedNoReserve(addr, size); + if (addr1 != addr) { + Printf("FATAL: ThreadSanitizer can not mmap thread trace (%p/%p->%p)\n", + addr, size, addr1); Die(); } } @@ -204,23 +230,21 @@ void Initialize(ThreadState *thr) { #endif InitializeFlags(&ctx->flags, env); // Setup correct file descriptor for error reports. - if (internal_strcmp(flags()->log_path, "stdout") == 0) - __sanitizer_set_report_fd(kStdoutFd); - else if (internal_strcmp(flags()->log_path, "stderr") == 0) - __sanitizer_set_report_fd(kStderrFd); - else - __sanitizer_set_report_path(flags()->log_path); + __sanitizer_set_report_path(flags()->log_path); InitializeSuppressions(); #ifndef TSAN_GO + InitializeLibIgnore(); // Initialize external symbolizer before internal threads are started. const char *external_symbolizer = flags()->external_symbolizer_path; - if (external_symbolizer != 0 && external_symbolizer[0] != '\0') { - if (!getSymbolizer()->InitializeExternal(external_symbolizer)) { - Printf("Failed to start external symbolizer: '%s'\n", - external_symbolizer); - Die(); - } + bool external_symbolizer_started = + Symbolizer::Init(external_symbolizer)->IsExternalAvailable(); + if (external_symbolizer != 0 && external_symbolizer[0] != '\0' && + !external_symbolizer_started) { + Printf("Failed to start external symbolizer: '%s'\n", + external_symbolizer); + Die(); } + Symbolizer::Get()->AddHooks(EnterSymbolizer, ExitSymbolizer); #endif internal_start_thread(&BackgroundThread, 0); @@ -636,9 +660,9 @@ void FuncEntry(ThreadState *thr, uptr pc) { // Shadow stack maintenance can be replaced with // stack unwinding during trace switch (which presumably must be faster). - DCHECK_GE(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GE(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #else if (thr->shadow_stack_pos == thr->shadow_stack_end) { const int sz = thr->shadow_stack_end - thr->shadow_stack; @@ -664,26 +688,52 @@ void FuncExit(ThreadState *thr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeFuncExit, 0); - DCHECK_GT(thr->shadow_stack_pos, &thr->shadow_stack[0]); + DCHECK_GT(thr->shadow_stack_pos, thr->shadow_stack); #ifndef TSAN_GO - DCHECK_LT(thr->shadow_stack_pos, &thr->shadow_stack[kShadowStackSize]); + DCHECK_LT(thr->shadow_stack_pos, thr->shadow_stack_end); #endif thr->shadow_stack_pos--; } -void ThreadIgnoreBegin(ThreadState *thr) { +void ThreadIgnoreBegin(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); thr->ignore_reads_and_writes++; - CHECK_GE(thr->ignore_reads_and_writes, 0); + CHECK_GT(thr->ignore_reads_and_writes, 0); thr->fast_state.SetIgnoreBit(); +#ifndef TSAN_GO + thr->mop_ignore_set.Add(CurrentStackId(thr, pc)); +#endif } -void ThreadIgnoreEnd(ThreadState *thr) { +void ThreadIgnoreEnd(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); thr->ignore_reads_and_writes--; CHECK_GE(thr->ignore_reads_and_writes, 0); - if (thr->ignore_reads_and_writes == 0) + if (thr->ignore_reads_and_writes == 0) { thr->fast_state.ClearIgnoreBit(); +#ifndef TSAN_GO + thr->mop_ignore_set.Reset(); +#endif + } +} + +void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc) { + DPrintf("#%d: ThreadIgnoreSyncBegin\n", thr->tid); + thr->ignore_sync++; + CHECK_GT(thr->ignore_sync, 0); +#ifndef TSAN_GO + thr->sync_ignore_set.Add(CurrentStackId(thr, pc)); +#endif +} + +void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc) { + DPrintf("#%d: ThreadIgnoreSyncEnd\n", thr->tid); + thr->ignore_sync--; + CHECK_GE(thr->ignore_sync, 0); +#ifndef TSAN_GO + if (thr->ignore_sync == 0) + thr->mop_ignore_set.Reset(); +#endif } bool MD5Hash::operator==(const MD5Hash &other) const { diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index 2548f67..45ed096 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -27,6 +27,7 @@ #include "sanitizer_common/sanitizer_allocator.h" #include "sanitizer_common/sanitizer_allocator_internal.h" #include "sanitizer_common/sanitizer_common.h" +#include "sanitizer_common/sanitizer_libignore.h" #include "sanitizer_common/sanitizer_suppressions.h" #include "sanitizer_common/sanitizer_thread_registry.h" #include "tsan_clock.h" @@ -38,6 +39,7 @@ #include "tsan_report.h" #include "tsan_platform.h" #include "tsan_mutexset.h" +#include "tsan_ignoreset.h" #if SANITIZER_WORDSIZE != 64 # error "ThreadSanitizer is supported only on 64-bit platforms" @@ -409,17 +411,19 @@ struct ThreadState { // We do not distinguish beteween ignoring reads and writes // for better performance. int ignore_reads_and_writes; - uptr *shadow_stack_pos; - u64 *racy_shadow_addr; - u64 racy_state[2]; + int ignore_sync; + // Go does not support ignores. #ifndef TSAN_GO - // C/C++ uses embed shadow stack of fixed size. - uptr shadow_stack[kShadowStackSize]; -#else - // Go uses satellite shadow stack with dynamic size. + IgnoreSet mop_ignore_set; + IgnoreSet sync_ignore_set; +#endif + // C/C++ uses fixed size shadow stack embed into Trace. + // Go uses malloc-allocated shadow stack with dynamic size. uptr *shadow_stack; uptr *shadow_stack_end; -#endif + uptr *shadow_stack_pos; + u64 *racy_shadow_addr; + u64 racy_state[2]; MutexSet mset; ThreadClock clock; #ifndef TSAN_GO @@ -432,6 +436,7 @@ struct ThreadState { const int unique_id; int in_rtl; bool in_symbolizer; + bool in_ignored_lib; bool is_alive; bool is_freeing; bool is_vptr_access; @@ -439,6 +444,7 @@ struct ThreadState { const uptr stk_size; const uptr tls_addr; const uptr tls_size; + ThreadContext *tctx; DeadlockDetector deadlock_detector; @@ -596,6 +602,7 @@ void MapThreadTrace(uptr addr, uptr size); void DontNeedShadowFor(uptr addr, uptr size); void InitializeShadowMemory(); void InitializeInterceptors(); +void InitializeLibIgnore(); void InitializeDynamicAnnotations(); void ReportRace(ThreadState *thr); @@ -625,6 +632,7 @@ ReportStack *SkipTsanInternalFrames(ReportStack *ent); #endif u32 CurrentStackId(ThreadState *thr, uptr pc); +ReportStack *SymbolizeStackId(u32 stack_id); void PrintCurrentStack(ThreadState *thr, uptr pc); void PrintCurrentStackSlow(); // uses libunwind @@ -675,8 +683,11 @@ void ALWAYS_INLINE MemoryWriteAtomic(ThreadState *thr, uptr pc, void MemoryResetRange(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeFreed(ThreadState *thr, uptr pc, uptr addr, uptr size); void MemoryRangeImitateWrite(ThreadState *thr, uptr pc, uptr addr, uptr size); -void ThreadIgnoreBegin(ThreadState *thr); -void ThreadIgnoreEnd(ThreadState *thr); + +void ThreadIgnoreBegin(ThreadState *thr, uptr pc); +void ThreadIgnoreEnd(ThreadState *thr, uptr pc); +void ThreadIgnoreSyncBegin(ThreadState *thr, uptr pc); +void ThreadIgnoreSyncEnd(ThreadState *thr, uptr pc); void FuncEntry(ThreadState *thr, uptr pc); void FuncExit(ThreadState *thr); @@ -700,12 +711,17 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all = false); void MutexReadLock(ThreadState *thr, uptr pc, uptr addr); void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr); void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr); +void MutexRepair(ThreadState *thr, uptr pc, uptr addr); // call on EOWNERDEAD void Acquire(ThreadState *thr, uptr pc, uptr addr); void AcquireGlobal(ThreadState *thr, uptr pc); void Release(ThreadState *thr, uptr pc, uptr addr); void ReleaseStore(ThreadState *thr, uptr pc, uptr addr); void AfterSleep(ThreadState *thr, uptr pc); +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c); +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c); // The hacky call uses custom calling convention and an assembly thunk. // It is considerably faster that a normal call for the caller @@ -718,11 +734,11 @@ void AfterSleep(ThreadState *thr, uptr pc); // so we create a reserve stack frame for it (1024b must be enough). #define HACKY_CALL(f) \ __asm__ __volatile__("sub $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset 1024;*/" \ + ".cfi_adjust_cfa_offset 1024;" \ ".hidden " #f "_thunk;" \ "call " #f "_thunk;" \ "add $1024, %%rsp;" \ - "/*.cfi_adjust_cfa_offset -1024;*/" \ + ".cfi_adjust_cfa_offset -1024;" \ ::: "memory", "cc"); #else #define HACKY_CALL(f) f() diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cc b/libsanitizer/tsan/tsan_rtl_mutex.cc index d274a7a..d9a3a3b 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cc +++ b/libsanitizer/tsan/tsan_rtl_mutex.cc @@ -98,11 +98,8 @@ void MutexLock(ThreadState *thr, uptr pc, uptr addr, int rec) { } if (s->recursion == 0) { StatInc(thr, StatMutexLock); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); - thr->clock.acquire(&s->read_clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); + AcquireImpl(thr, pc, &s->read_clock); } else if (!s->is_recursive) { StatInc(thr, StatMutexRecLock); } @@ -139,10 +136,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, bool all) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } @@ -166,10 +160,8 @@ void MutexReadLock(ThreadState *thr, uptr pc, uptr addr) { addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); + AcquireImpl(thr, pc, &s->clock); s->last_lock = thr->fast_state.raw(); - StatInc(thr, StatSyncAcquire); thr->mset.Add(s->GetId(), false, thr->fast_state.epoch()); s->mtx.ReadUnlock(); } @@ -188,10 +180,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { addr); PrintCurrentStack(thr, pc); } - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); s->mtx.Unlock(); thr->mset.Del(s->GetId(), false); } @@ -209,10 +198,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { StatInc(thr, StatMutexReadUnlock); thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&s->read_clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->read_clock); } else if (s->owner_tid == thr->tid) { // Seems to be write unlock. thr->fast_state.IncrementEpoch(); @@ -222,14 +208,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); s->owner_tid = SyncVar::kInvalidTid; - // FIXME: Refactor me, plz. - // The sequence of events is quite tricky and doubled in several places. - // First, it's a bug to increment the epoch w/o writing to the trace. - // Then, the acquire/release logic can be factored out as well. - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); } @@ -243,13 +222,23 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { s->mtx.Unlock(); } +void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { + Context *ctx = CTX(); + CHECK_GT(thr->in_rtl, 0); + DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); + SyncVar *s = ctx->synctab.GetOrCreateAndLock(thr, pc, addr, true); + s->owner_tid = SyncVar::kInvalidTid; + s->recursion = 0; + s->mtx.Unlock(); +} + void Acquire(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Acquire %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, false); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.acquire(&s->clock); - StatInc(thr, StatSyncAcquire); + AcquireImpl(thr, pc, &s->clock); s->mtx.ReadUnlock(); } @@ -263,6 +252,9 @@ static void UpdateClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AcquireGlobal(ThreadState *thr, uptr pc) { + DPrintf("#%d: AcquireGlobal\n", thr->tid); + if (thr->ignore_sync) + return; ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( UpdateClockCallback, thr); @@ -271,20 +263,26 @@ void AcquireGlobal(ThreadState *thr, uptr pc) { void Release(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: Release %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.release(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseImpl(thr, pc, &s->clock); s->mtx.Unlock(); } void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) { CHECK_GT(thr->in_rtl, 0); DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr); + if (thr->ignore_sync) + return; SyncVar *s = CTX()->synctab.GetOrCreateAndLock(thr, pc, addr, true); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->clock.ReleaseStore(&s->clock); - StatInc(thr, StatSyncRelease); + thr->fast_state.IncrementEpoch(); + // Can't increment epoch w/o writing to the trace as well. + TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); + ReleaseStoreImpl(thr, pc, &s->clock); s->mtx.Unlock(); } @@ -299,6 +297,9 @@ static void UpdateSleepClockCallback(ThreadContextBase *tctx_base, void *arg) { } void AfterSleep(ThreadState *thr, uptr pc) { + DPrintf("#%d: AfterSleep %zx\n", thr->tid); + if (thr->ignore_sync) + return; thr->last_sleep_stack_id = CurrentStackId(thr, pc); ThreadRegistryLock l(CTX()->thread_registry); CTX()->thread_registry->RunCallbackForEachThreadLocked( @@ -306,4 +307,40 @@ void AfterSleep(ThreadState *thr, uptr pc) { } #endif +void AcquireImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->clock.acquire(c); + StatInc(thr, StatSyncAcquire); +} + +void ReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.release(c); + StatInc(thr, StatSyncRelease); +} + +void ReleaseStoreImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.ReleaseStore(c); + StatInc(thr, StatSyncRelease); +} + +void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { + if (thr->ignore_sync) + return; + thr->clock.set(thr->tid, thr->fast_state.epoch()); + thr->fast_synch_epoch = thr->fast_state.epoch(); + thr->clock.acq_rel(c); + StatInc(thr, StatSyncAcquire); + StatInc(thr, StatSyncRelease); +} + } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_rtl_report.cc b/libsanitizer/tsan/tsan_rtl_report.cc index 7c0a028..f2248af 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cc +++ b/libsanitizer/tsan/tsan_rtl_report.cc @@ -99,6 +99,18 @@ static void StackStripMain(ReportStack *stack) { #endif } +#ifndef TSAN_GO +ReportStack *SymbolizeStackId(u32 stack_id) { + uptr ssz = 0; + const uptr *stack = StackDepotGet(stack_id, &ssz); + if (stack == 0) + return 0; + StackTrace trace; + trace.Init(stack, ssz); + return SymbolizeStack(trace); +} +#endif + static ReportStack *SymbolizeStack(const StackTrace& trace) { if (trace.IsEmpty()) return 0; @@ -201,13 +213,7 @@ void ScopedReport::AddThread(const ThreadContext *tctx) { #ifdef TSAN_GO rt->stack = SymbolizeStack(tctx->creation_stack); #else - uptr ssz = 0; - const uptr *stack = StackDepotGet(tctx->creation_stack_id, &ssz); - if (stack) { - StackTrace trace; - trace.Init(stack, ssz); - rt->stack = SymbolizeStack(trace); - } + rt->stack = SymbolizeStackId(tctx->creation_stack_id); #endif } @@ -270,13 +276,7 @@ void ScopedReport::AddMutex(const SyncVar *s) { rm->destroyed = false; rm->stack = 0; #ifndef TSAN_GO - uptr ssz = 0; - const uptr *stack = StackDepotGet(s->creation_stack_id, &ssz); - if (stack) { - StackTrace trace; - trace.Init(stack, ssz); - rm->stack = SymbolizeStack(trace); - } + rm->stack = SymbolizeStackId(s->creation_stack_id); #endif } @@ -308,13 +308,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) { loc->type = ReportLocationFD; loc->fd = fd; loc->tid = creat_tid; - uptr ssz = 0; - const uptr *stack = StackDepotGet(creat_stack, &ssz); - if (stack) { - StackTrace trace; - trace.Init(stack, ssz); - loc->stack = SymbolizeStack(trace); - } + loc->stack = SymbolizeStackId(creat_stack); ThreadContext *tctx = FindThreadByUidLocked(creat_tid); if (tctx) AddThread(tctx); @@ -335,13 +329,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) { loc->file = 0; loc->line = 0; loc->stack = 0; - uptr ssz = 0; - const uptr *stack = StackDepotGet(b->StackId(), &ssz); - if (stack) { - StackTrace trace; - trace.Init(stack, ssz); - loc->stack = SymbolizeStack(trace); - } + loc->stack = SymbolizeStackId(b->StackId()); if (tctx) AddThread(tctx); return; @@ -365,13 +353,7 @@ void ScopedReport::AddLocation(uptr addr, uptr size) { #ifndef TSAN_GO void ScopedReport::AddSleep(u32 stack_id) { - uptr ssz = 0; - const uptr *stack = StackDepotGet(stack_id, &ssz); - if (stack) { - StackTrace trace; - trace.Init(stack, ssz); - rep_->sleep = SymbolizeStack(trace); - } + rep_->sleep = SymbolizeStackId(stack_id); } #endif @@ -408,7 +390,7 @@ void RestoreStack(int tid, const u64 epoch, StackTrace *stk, MutexSet *mset) { const u64 ebegin = RoundDown(eend, kTracePartSize); DPrintf("#%d: RestoreStack epoch=%zu ebegin=%zu eend=%zu partidx=%d\n", tid, (uptr)epoch, (uptr)ebegin, (uptr)eend, partidx); - InternalScopedBuffer stack(1024); // FIXME: de-hardcode 1024 + InternalScopedBuffer stack(kShadowStackSize); for (uptr i = 0; i < hdr->stack0.Size(); i++) { stack[i] = hdr->stack0.Get(i); DPrintf2(" #%02lu: pc=%zx\n", i, stack[i]); @@ -724,8 +706,8 @@ void PrintCurrentStackSlow() { #ifndef TSAN_GO __sanitizer::StackTrace *ptrace = new(internal_alloc(MBlockStackTrace, sizeof(__sanitizer::StackTrace))) __sanitizer::StackTrace; - ptrace->SlowUnwindStack(__sanitizer::StackTrace::GetCurrentPc(), - kStackTraceMax); + ptrace->Unwind(kStackTraceMax, __sanitizer::StackTrace::GetCurrentPc(), + 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]; diff --git a/libsanitizer/tsan/tsan_rtl_thread.cc b/libsanitizer/tsan/tsan_rtl_thread.cc index 9811e1c..dea6698 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cc +++ b/libsanitizer/tsan/tsan_rtl_thread.cc @@ -39,8 +39,7 @@ void ThreadContext::OnDead() { void ThreadContext::OnJoined(void *arg) { ThreadState *caller_thr = static_cast(arg); - caller_thr->clock.acquire(&sync); - StatInc(caller_thr, StatSyncAcquire); + AcquireImpl(caller_thr, 0, &sync); sync.Reset(); } @@ -57,10 +56,7 @@ void ThreadContext::OnCreated(void *arg) { args->thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(args->thr, args->thr->fast_state, EventTypeMop, 0); - args->thr->clock.set(args->thr->tid, args->thr->fast_state.epoch()); - args->thr->fast_synch_epoch = args->thr->fast_state.epoch(); - args->thr->clock.release(&sync); - StatInc(args->thr, StatSyncRelease); + ReleaseImpl(args->thr, 0, &sync); #ifdef TSAN_GO creation_stack.ObtainCurrent(args->thr, args->pc); #else @@ -93,21 +89,23 @@ void ThreadContext::OnStarted(void *arg) { epoch1 = (u64)-1; new(thr) ThreadState(CTX(), tid, unique_id, epoch0, args->stk_addr, args->stk_size, args->tls_addr, args->tls_size); -#ifdef TSAN_GO +#ifndef TSAN_GO + thr->shadow_stack = &ThreadTrace(thr->tid)->shadow_stack[0]; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kShadowStackSize; +#else // Setup dynamic shadow stack. const int kInitStackSize = 8; - args->thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, + thr->shadow_stack = (uptr*)internal_alloc(MBlockShadowStack, kInitStackSize * sizeof(uptr)); - args->thr->shadow_stack_pos = thr->shadow_stack; - args->thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; + thr->shadow_stack_pos = thr->shadow_stack; + thr->shadow_stack_end = thr->shadow_stack + kInitStackSize; #endif #ifndef TSAN_GO - AllocatorThreadStart(args->thr); + AllocatorThreadStart(thr); #endif - thr = args->thr; thr->fast_synch_epoch = epoch0; - thr->clock.set(tid, epoch0); - thr->clock.acquire(&sync); + AcquireImpl(thr, 0, &sync); thr->fast_state.SetHistorySize(flags()->history_size); const uptr trace = (epoch0 / kTracePartSize) % TraceParts(); Trace *thr_trace = ThreadTrace(thr->tid); @@ -126,10 +124,7 @@ void ThreadContext::OnFinished() { thr->fast_state.IncrementEpoch(); // Can't increment epoch w/o writing to the trace as well. TraceAddEvent(thr, thr->fast_state, EventTypeMop, 0); - thr->clock.set(thr->tid, thr->fast_state.epoch()); - thr->fast_synch_epoch = thr->fast_state.epoch(); - thr->clock.release(&sync); - StatInc(thr, StatSyncRelease); + ReleaseImpl(thr, 0, &sync); } epoch1 = thr->fast_state.epoch(); @@ -163,13 +158,34 @@ static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { } #endif -static void ThreadCheckIgnore(ThreadState *thr) { - if (thr->ignore_reads_and_writes) { - Printf("ThreadSanitizer: thread T%d finished with ignores enabled.\n", - thr->tid); +#ifndef TSAN_GO +static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { + if (tctx->tid == 0) { + Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); + } else { + Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled," + " created at:\n", tctx->tid, tctx->name); + PrintStack(SymbolizeStackId(tctx->creation_stack_id)); + } + Printf(" One of the following ignores was not ended" + " (in order of probability)\n"); + for (uptr i = 0; i < set->Size(); i++) { + Printf(" Ignore was enabled at:\n"); + PrintStack(SymbolizeStackId(set->At(i))); } + Die(); } +static void ThreadCheckIgnore(ThreadState *thr) { + if (thr->ignore_reads_and_writes) + ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set); + if (thr->ignore_sync) + ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set); +} +#else +static void ThreadCheckIgnore(ThreadState *thr) {} +#endif + void ThreadFinalize(ThreadState *thr) { CHECK_GT(thr->in_rtl, 0); ThreadCheckIgnore(thr); @@ -209,6 +225,7 @@ int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { } void ThreadStart(ThreadState *thr, int tid, uptr os_id) { + Context *ctx = CTX(); CHECK_GT(thr->in_rtl, 0); uptr stk_addr = 0; uptr stk_size = 0; @@ -235,8 +252,13 @@ void ThreadStart(ThreadState *thr, int tid, uptr os_id) { } } + ThreadRegistry *tr = ctx->thread_registry; OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; - CTX()->thread_registry->StartThread(tid, os_id, &args); + tr->StartThread(tid, os_id, &args); + + tr->Lock(); + thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid); + tr->Unlock(); } void ThreadFinish(ThreadState *thr) { diff --git a/libsanitizer/tsan/tsan_stat.cc b/libsanitizer/tsan/tsan_stat.cc index 3a3ac11..cdd11ca 100644 --- a/libsanitizer/tsan/tsan_stat.cc +++ b/libsanitizer/tsan/tsan_stat.cc @@ -140,6 +140,7 @@ void StatOutput(u64 *stat) { name[StatInt_strcasecmp] = " strcasecmp "; name[StatInt_strncasecmp] = " strncasecmp "; name[StatInt_atexit] = " atexit "; + name[StatInt__exit] = " _exit "; name[StatInt___cxa_guard_acquire] = " __cxa_guard_acquire "; name[StatInt___cxa_guard_release] = " __cxa_guard_release "; name[StatInt___cxa_guard_abort] = " __cxa_guard_abort "; @@ -179,6 +180,7 @@ void StatOutput(u64 *stat) { name[StatInt_pthread_barrier_wait] = " pthread_barrier_wait "; name[StatInt_pthread_once] = " pthread_once "; name[StatInt_pthread_getschedparam] = " pthread_getschedparam "; + name[StatInt_pthread_setname_np] = " pthread_setname_np "; name[StatInt_sem_init] = " sem_init "; name[StatInt_sem_destroy] = " sem_destroy "; name[StatInt_sem_wait] = " sem_wait "; @@ -288,6 +290,7 @@ void StatOutput(u64 *stat) { name[StatInt_ctime_r] = " ctime_r "; name[StatInt_asctime] = " asctime "; name[StatInt_asctime_r] = " asctime_r "; + name[StatInt_strptime] = " strptime "; name[StatInt_frexp] = " frexp "; name[StatInt_frexpf] = " frexpf "; name[StatInt_frexpl] = " frexpl "; @@ -356,6 +359,7 @@ void StatOutput(u64 *stat) { name[StatInt_sched_getaffinity] = " sched_getaffinity "; name[StatInt_strerror] = " strerror "; name[StatInt_strerror_r] = " strerror_r "; + name[StatInt___xpg_strerror_r] = " __xpg_strerror_r "; name[StatInt_scandir] = " scandir "; name[StatInt_scandir64] = " scandir64 "; name[StatInt_getgroups] = " getgroups "; @@ -369,6 +373,59 @@ void StatOutput(u64 *stat) { name[StatInt_sigprocmask] = " sigprocmask "; name[StatInt_backtrace] = " backtrace "; name[StatInt_backtrace_symbols] = " backtrace_symbols "; + name[StatInt_dlopen] = " dlopen "; + name[StatInt_dlclose] = " dlclose "; + name[StatInt_getmntent] = " getmntent "; + name[StatInt_getmntent_r] = " getmntent_r "; + name[StatInt_statfs] = " statfs "; + name[StatInt_statfs64] = " statfs64 "; + name[StatInt_fstatfs] = " fstatfs "; + name[StatInt_fstatfs64] = " fstatfs64 "; + name[StatInt_statvfs] = " statvfs "; + name[StatInt_statvfs64] = " statvfs64 "; + name[StatInt_fstatvfs] = " fstatvfs "; + name[StatInt_fstatvfs64] = " fstatvfs64 "; + name[StatInt_initgroups] = " initgroups "; + name[StatInt_ether_ntoa] = " ether_ntoa "; + name[StatInt_ether_aton] = " ether_aton "; + name[StatInt_ether_ntoa_r] = " ether_ntoa_r "; + name[StatInt_ether_aton_r] = " ether_aton_r "; + name[StatInt_ether_ntohost] = " ether_ntohost "; + name[StatInt_ether_hostton] = " ether_hostton "; + name[StatInt_ether_line] = " ether_line "; + name[StatInt_shmctl] = " shmctl "; + name[StatInt_random_r] = " random_r "; + name[StatInt_tmpnam] = " tmpnam "; + name[StatInt_tmpnam_r] = " tmpnam_r "; + name[StatInt_tempnam] = " tempnam "; + name[StatInt_sincos] = " sincos "; + name[StatInt_sincosf] = " sincosf "; + name[StatInt_sincosl] = " sincosl "; + name[StatInt_remquo] = " remquo "; + name[StatInt_remquof] = " remquof "; + name[StatInt_remquol] = " remquol "; + name[StatInt_lgamma] = " lgamma "; + name[StatInt_lgammaf] = " lgammaf "; + name[StatInt_lgammal] = " lgammal "; + name[StatInt_lgamma_r] = " lgamma_r "; + name[StatInt_lgammaf_r] = " lgammaf_r "; + name[StatInt_lgammal_r] = " lgammal_r "; + name[StatInt_drand48_r] = " drand48_r "; + name[StatInt_lrand48_r] = " lrand48_r "; + name[StatInt_getline] = " getline "; + name[StatInt_getdelim] = " getdelim "; + name[StatInt_iconv] = " iconv "; + name[StatInt_times] = " times "; + + name[StatInt_pthread_attr_getdetachstate] = " pthread_addr_getdetachstate "; // NOLINT + name[StatInt_pthread_attr_getguardsize] = " pthread_addr_getguardsize "; // NOLINT + name[StatInt_pthread_attr_getschedparam] = " pthread_addr_getschedparam "; // NOLINT + name[StatInt_pthread_attr_getschedpolicy] = " pthread_addr_getschedpolicy "; // NOLINT + name[StatInt_pthread_attr_getinheritsched] = " pthread_addr_getinheritsched "; // NOLINT + name[StatInt_pthread_attr_getscope] = " pthread_addr_getscope "; // NOLINT + name[StatInt_pthread_attr_getstacksize] = " pthread_addr_getstacksize "; // NOLINT + name[StatInt_pthread_attr_getstack] = " pthread_addr_getstack "; // NOLINT + name[StatInt_pthread_attr_getaffinity_np] = " pthread_addr_getaffinity_np "; // NOLINT name[StatAnnotation] = "Dynamic annotations "; name[StatAnnotateHappensBefore] = " HappensBefore "; @@ -400,6 +457,8 @@ void StatOutput(u64 *stat) { name[StatAnnotateIgnoreReadsEnd] = " IgnoreReadsEnd "; name[StatAnnotateIgnoreWritesBegin] = " IgnoreWritesBegin "; name[StatAnnotateIgnoreWritesEnd] = " IgnoreWritesEnd "; + name[StatAnnotateIgnoreSyncBegin] = " IgnoreSyncBegin "; + name[StatAnnotateIgnoreSyncEnd] = " IgnoreSyncEnd "; name[StatAnnotatePublishMemoryRange] = " PublishMemoryRange "; name[StatAnnotateUnpublishMemoryRange] = " UnpublishMemoryRange "; name[StatAnnotateThreadName] = " ThreadName "; diff --git a/libsanitizer/tsan/tsan_stat.h b/libsanitizer/tsan/tsan_stat.h index e392ff6..998f1cd 100644 --- a/libsanitizer/tsan/tsan_stat.h +++ b/libsanitizer/tsan/tsan_stat.h @@ -137,6 +137,7 @@ enum StatType { StatInt_strstr, StatInt_strdup, StatInt_atexit, + StatInt__exit, StatInt___cxa_guard_acquire, StatInt___cxa_guard_release, StatInt___cxa_guard_abort, @@ -174,6 +175,7 @@ enum StatType { StatInt_pthread_barrier_wait, StatInt_pthread_once, StatInt_pthread_getschedparam, + StatInt_pthread_setname_np, StatInt_sem_init, StatInt_sem_destroy, StatInt_sem_wait, @@ -283,6 +285,7 @@ enum StatType { StatInt_ctime_r, StatInt_asctime, StatInt_asctime_r, + StatInt_strptime, StatInt_frexp, StatInt_frexpf, StatInt_frexpl, @@ -351,6 +354,7 @@ enum StatType { StatInt_sched_getaffinity, StatInt_strerror, StatInt_strerror_r, + StatInt___xpg_strerror_r, StatInt_scandir, StatInt_scandir64, StatInt_getgroups, @@ -364,6 +368,59 @@ enum StatType { StatInt_sigprocmask, StatInt_backtrace, StatInt_backtrace_symbols, + StatInt_dlopen, + StatInt_dlclose, + StatInt_getmntent, + StatInt_getmntent_r, + StatInt_statfs, + StatInt_statfs64, + StatInt_fstatfs, + StatInt_fstatfs64, + StatInt_statvfs, + StatInt_statvfs64, + StatInt_fstatvfs, + StatInt_fstatvfs64, + StatInt_initgroups, + StatInt_ether_ntoa, + StatInt_ether_aton, + StatInt_ether_ntoa_r, + StatInt_ether_aton_r, + StatInt_ether_ntohost, + StatInt_ether_hostton, + StatInt_ether_line, + StatInt_shmctl, + StatInt_random_r, + StatInt_tmpnam, + StatInt_tmpnam_r, + StatInt_tempnam, + StatInt_sincos, + StatInt_sincosf, + StatInt_sincosl, + StatInt_remquo, + StatInt_remquof, + StatInt_remquol, + StatInt_lgamma, + StatInt_lgammaf, + StatInt_lgammal, + StatInt_lgamma_r, + StatInt_lgammaf_r, + StatInt_lgammal_r, + StatInt_drand48_r, + StatInt_lrand48_r, + StatInt_getline, + StatInt_getdelim, + StatInt_iconv, + StatInt_times, + + StatInt_pthread_attr_getdetachstate, + StatInt_pthread_attr_getguardsize, + StatInt_pthread_attr_getschedparam, + StatInt_pthread_attr_getschedpolicy, + StatInt_pthread_attr_getinheritsched, + StatInt_pthread_attr_getscope, + StatInt_pthread_attr_getstacksize, + StatInt_pthread_attr_getstack, + StatInt_pthread_attr_getaffinity_np, // Dynamic annotations. StatAnnotation, @@ -396,6 +453,8 @@ enum StatType { StatAnnotateIgnoreReadsEnd, StatAnnotateIgnoreWritesBegin, StatAnnotateIgnoreWritesEnd, + StatAnnotateIgnoreSyncBegin, + StatAnnotateIgnoreSyncEnd, StatAnnotatePublishMemoryRange, StatAnnotateUnpublishMemoryRange, StatAnnotateThreadName, diff --git a/libsanitizer/tsan/tsan_suppressions.cc b/libsanitizer/tsan/tsan_suppressions.cc index 912b383..fa0c30d 100644 --- a/libsanitizer/tsan/tsan_suppressions.cc +++ b/libsanitizer/tsan/tsan_suppressions.cc @@ -85,6 +85,11 @@ void InitializeSuppressions() { #endif } +SuppressionContext *GetSuppressionContext() { + CHECK_NE(g_ctx, 0); + return g_ctx; +} + SuppressionType conv(ReportType typ) { if (typ == ReportTypeRace) return SuppressionRace; diff --git a/libsanitizer/tsan/tsan_suppressions.h b/libsanitizer/tsan/tsan_suppressions.h index e38d81e..2939e9a 100644 --- a/libsanitizer/tsan/tsan_suppressions.h +++ b/libsanitizer/tsan/tsan_suppressions.h @@ -20,6 +20,7 @@ void InitializeSuppressions(); void PrintMatchedSuppressions(); uptr IsSuppressed(ReportType typ, const ReportStack *stack, Suppression **sp); uptr IsSuppressed(ReportType typ, const ReportLocation *loc, Suppression **sp); +SuppressionContext *GetSuppressionContext(); } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_symbolize.cc b/libsanitizer/tsan/tsan_symbolize.cc index 0c7efac7..c0e794b 100644 --- a/libsanitizer/tsan/tsan_symbolize.cc +++ b/libsanitizer/tsan/tsan_symbolize.cc @@ -20,19 +20,17 @@ namespace __tsan { -struct ScopedInSymbolizer { - ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(!thr->in_symbolizer); - thr->in_symbolizer = true; - } +void EnterSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(!thr->in_symbolizer); + thr->in_symbolizer = true; +} - ~ScopedInSymbolizer() { - ThreadState *thr = cur_thread(); - CHECK(thr->in_symbolizer); - thr->in_symbolizer = false; - } -}; +void ExitSymbolizer() { + ThreadState *thr = cur_thread(); + CHECK(thr->in_symbolizer); + thr->in_symbolizer = false; +} ReportStack *NewReportStackEntry(uptr addr) { ReportStack *ent = (ReportStack*)internal_alloc(MBlockReportStack, @@ -42,18 +40,6 @@ ReportStack *NewReportStackEntry(uptr addr) { return ent; } -// Strip module path to make output shorter. -static 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 ReportStack *NewReportStackEntry(const AddressInfo &info) { ReportStack *ent = NewReportStackEntry(info.address); ent->module = StripModuleName(info.module); @@ -117,15 +103,14 @@ ReportStack *SymbolizeCode(uptr addr) { ent->col = col; return ent; } - if (!getSymbolizer()->IsAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return SymbolizeCodeAddr2Line(addr); - ScopedInSymbolizer in_symbolizer; static const uptr kMaxAddrFrames = 16; InternalScopedBuffer addr_frames(kMaxAddrFrames); for (uptr i = 0; i < kMaxAddrFrames; i++) new(&addr_frames[i]) AddressInfo(); - uptr addr_frames_num = - getSymbolizer()->SymbolizeCode(addr, addr_frames.data(), kMaxAddrFrames); + uptr addr_frames_num = Symbolizer::Get()->SymbolizeCode( + addr, addr_frames.data(), kMaxAddrFrames); if (addr_frames_num == 0) return NewReportStackEntry(addr); ReportStack *top = 0; @@ -144,11 +129,10 @@ ReportStack *SymbolizeCode(uptr addr) { } ReportLocation *SymbolizeData(uptr addr) { - if (!getSymbolizer()->IsAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return 0; - ScopedInSymbolizer in_symbolizer; DataInfo info; - if (!getSymbolizer()->SymbolizeData(addr, &info)) + if (!Symbolizer::Get()->SymbolizeData(addr, &info)) return 0; ReportLocation *ent = (ReportLocation*)internal_alloc(MBlockReportStack, sizeof(ReportLocation)); @@ -164,10 +148,9 @@ ReportLocation *SymbolizeData(uptr addr) { } void SymbolizeFlush() { - if (!getSymbolizer()->IsAvailable()) + if (!Symbolizer::Get()->IsAvailable()) return; - ScopedInSymbolizer in_symbolizer; - getSymbolizer()->Flush(); + Symbolizer::Get()->Flush(); } } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_symbolize.h b/libsanitizer/tsan/tsan_symbolize.h index c610d1f..892c11c 100644 --- a/libsanitizer/tsan/tsan_symbolize.h +++ b/libsanitizer/tsan/tsan_symbolize.h @@ -16,6 +16,8 @@ namespace __tsan { +void EnterSymbolizer(); +void ExitSymbolizer(); ReportStack *SymbolizeCode(uptr addr); ReportLocation *SymbolizeData(uptr addr); void SymbolizeFlush(); diff --git a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc index da0c87d..c278a42 100644 --- a/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc +++ b/libsanitizer/tsan/tsan_symbolize_addr2line_linux.cc @@ -58,7 +58,6 @@ static void NOINLINE InitModule(ModuleDesc *m) { } int pid = fork(); if (pid == 0) { - __sanitizer_set_report_fd(STDERR_FILENO); internal_close(STDOUT_FILENO); internal_close(STDIN_FILENO); internal_dup2(outfd[0], STDIN_FILENO); diff --git a/libsanitizer/tsan/tsan_sync.cc b/libsanitizer/tsan/tsan_sync.cc index 04fef61..0c5be10 100644 --- a/libsanitizer/tsan/tsan_sync.cc +++ b/libsanitizer/tsan/tsan_sync.cc @@ -263,6 +263,11 @@ void StackTrace::ObtainCurrent(ThreadState *thr, uptr 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])); } diff --git a/libsanitizer/tsan/tsan_trace.h b/libsanitizer/tsan/tsan_trace.h index 6986483..93ed8d9 100644 --- a/libsanitizer/tsan/tsan_trace.h +++ b/libsanitizer/tsan/tsan_trace.h @@ -60,6 +60,11 @@ struct TraceHeader { struct Trace { TraceHeader headers[kTraceParts]; Mutex mtx; +#ifndef TSAN_GO + // Must be last to catch overflow as paging fault. + // Go shadow stack is dynamically allocated. + uptr shadow_stack[kShadowStackSize]; +#endif Trace() : mtx(MutexTypeTrace, StatMtxTrace) { diff --git a/libsanitizer/ubsan/ubsan_diag.cc b/libsanitizer/ubsan/ubsan_diag.cc index c7378fd..786ffa7 100644 --- a/libsanitizer/ubsan/ubsan_diag.cc +++ b/libsanitizer/ubsan/ubsan_diag.cc @@ -24,12 +24,21 @@ Location __ubsan::getCallerLocation(uptr CallerLoc) { return Location(); uptr Loc = StackTrace::GetPreviousInstructionPc(CallerLoc); + return getFunctionLocation(Loc, 0); +} + +Location __ubsan::getFunctionLocation(uptr Loc, const char **FName) { + if (!Loc) + return Location(); AddressInfo Info; - if (!getSymbolizer()->SymbolizeCode(Loc, &Info, 1) || + if (!Symbolizer::GetOrInit()->SymbolizeCode(Loc, &Info, 1) || !Info.module || !*Info.module) return Location(Loc); + if (FName && Info.function) + *FName = Info.function; + if (!Info.file) return ModuleLocation(Info.module, Info.module_offset); @@ -66,29 +75,29 @@ static void PrintHex(UIntMax Val) { } static void renderLocation(Location Loc) { + InternalScopedString LocBuffer(1024); switch (Loc.getKind()) { case Location::LK_Source: { SourceLocation SLoc = Loc.getSourceLocation(); if (SLoc.isInvalid()) - Printf(":"); - else { - Printf("%s:%d:", SLoc.getFilename(), SLoc.getLine()); - if (SLoc.getColumn()) - Printf("%d:", SLoc.getColumn()); - } + LocBuffer.append(""); + else + PrintSourceLocation(&LocBuffer, SLoc.getFilename(), SLoc.getLine(), + SLoc.getColumn()); break; } case Location::LK_Module: - Printf("%s:0x%zx:", Loc.getModuleLocation().getModuleName(), - Loc.getModuleLocation().getOffset()); + PrintModuleAndOffset(&LocBuffer, Loc.getModuleLocation().getModuleName(), + Loc.getModuleLocation().getOffset()); break; case Location::LK_Memory: - Printf("%p:", Loc.getMemoryLocation()); + LocBuffer.append("%p", Loc.getMemoryLocation()); break; case Location::LK_Null: - Printf(":"); + LocBuffer.append(""); break; } + Printf("%s:", LocBuffer.data()); } static void renderText(const char *Message, const Diag::Arg *Args) { @@ -108,7 +117,7 @@ static void renderText(const char *Message, const Diag::Arg *Args) { Printf("%s", A.String); break; case Diag::AK_Mangled: { - Printf("'%s'", getSymbolizer()->Demangle(A.String)); + Printf("'%s'", Symbolizer::GetOrInit()->Demangle(A.String)); break; } case Diag::AK_SInt: diff --git a/libsanitizer/ubsan/ubsan_diag.h b/libsanitizer/ubsan/ubsan_diag.h index 969d51c..0450368 100644 --- a/libsanitizer/ubsan/ubsan_diag.h +++ b/libsanitizer/ubsan/ubsan_diag.h @@ -78,6 +78,12 @@ public: /// an invalid location or a module location for the caller. Location getCallerLocation(uptr CallerLoc = GET_CALLER_PC()); +/// Try to obtain a location for the given function pointer. This might fail, +/// and produce either an invalid location or a module location for the caller. +/// If FName is non-null and the name of the function is known, set *FName to +/// the function name, otherwise *FName is unchanged. +Location getFunctionLocation(uptr Loc, const char **FName); + /// A diagnostic severity level. enum DiagLevel { DL_Error, ///< An error. diff --git a/libsanitizer/ubsan/ubsan_handlers.cc b/libsanitizer/ubsan/ubsan_handlers.cc index 5947c2a..dd2e7bb 100644 --- a/libsanitizer/ubsan/ubsan_handlers.cc +++ b/libsanitizer/ubsan/ubsan_handlers.cc @@ -244,15 +244,36 @@ void __ubsan::__ubsan_handle_float_cast_overflow_abort( void __ubsan::__ubsan_handle_load_invalid_value(InvalidValueData *Data, ValueHandle Val) { - // TODO: Add deduplication once a SourceLocation is generated for this check. - Diag(getCallerLocation(), DL_Error, + SourceLocation Loc = Data->Loc.acquire(); + if (Loc.isDisabled()) + return; + + Diag(Loc, DL_Error, "load of value %0, which is not a valid value for type %1") << Value(Data->Type, Val) << Data->Type; } void __ubsan::__ubsan_handle_load_invalid_value_abort(InvalidValueData *Data, ValueHandle Val) { - Diag(getCallerLocation(), DL_Error, - "load of value %0, which is not a valid value for type %1") - << Value(Data->Type, Val) << Data->Type; + __ubsan_handle_load_invalid_value(Data, Val); + Die(); +} + +void __ubsan::__ubsan_handle_function_type_mismatch( + FunctionTypeMismatchData *Data, + ValueHandle Function) { + const char *FName = "(unknown)"; + + Location Loc = getFunctionLocation(Function, &FName); + + Diag(Data->Loc, DL_Error, + "call to function %0 through pointer to incorrect function type %1") + << FName << Data->Type; + Diag(Loc, DL_Note, "%0 defined here") << FName; +} + +void __ubsan::__ubsan_handle_function_type_mismatch_abort( + FunctionTypeMismatchData *Data, + ValueHandle Function) { + __ubsan_handle_function_type_mismatch(Data, Function); Die(); } diff --git a/libsanitizer/ubsan/ubsan_handlers.h b/libsanitizer/ubsan/ubsan_handlers.h index 034edf5..226faad 100644 --- a/libsanitizer/ubsan/ubsan_handlers.h +++ b/libsanitizer/ubsan/ubsan_handlers.h @@ -103,13 +103,22 @@ struct FloatCastOverflowData { RECOVERABLE(float_cast_overflow, FloatCastOverflowData *Data, ValueHandle From) struct InvalidValueData { - // FIXME: SourceLocation Loc; + SourceLocation Loc; const TypeDescriptor &Type; }; /// \brief Handle a load of an invalid value for the type. RECOVERABLE(load_invalid_value, InvalidValueData *Data, ValueHandle Val) +struct FunctionTypeMismatchData { + SourceLocation Loc; + const TypeDescriptor &Type; +}; + +RECOVERABLE(function_type_mismatch, + FunctionTypeMismatchData *Data, + ValueHandle Val) + } #endif // UBSAN_HANDLERS_H diff --git a/libsanitizer/ubsan/ubsan_type_hash.cc b/libsanitizer/ubsan/ubsan_type_hash.cc index 440d3ad..d010094 100644 --- a/libsanitizer/ubsan/ubsan_type_hash.cc +++ b/libsanitizer/ubsan/ubsan_type_hash.cc @@ -83,16 +83,18 @@ namespace abi = __cxxabiv1; // reused as needed. The second caching layer is a large hash table with open // chaining. We can freely evict from either layer since this is just a cache. // -// FIXME: Make these hash table accesses thread-safe. The races here are benign -// (worst-case, we could miss a bug or see a slowdown) but we should -// avoid upsetting race detectors. +// FIXME: Make these hash table accesses thread-safe. The races here are benign: +// assuming the unsequenced loads and stores don't misbehave too badly, +// the worst case is false negatives or poor cache behavior, not false +// positives or crashes. /// Find a bucket to store the given hash value in. static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { static const unsigned HashTableSize = 65537; - static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize] = { 1 }; + static __ubsan::HashValue __ubsan_vptr_hash_set[HashTableSize]; - unsigned Probe = V & 65535; + unsigned First = (V & 65535) ^ 1; + unsigned Probe = First; for (int Tries = 5; Tries; --Tries) { if (!__ubsan_vptr_hash_set[Probe] || __ubsan_vptr_hash_set[Probe] == V) return &__ubsan_vptr_hash_set[Probe]; @@ -102,12 +104,12 @@ static __ubsan::HashValue *getTypeCacheHashTableBucket(__ubsan::HashValue V) { } // FIXME: Pick a random entry from the probe sequence to evict rather than // just taking the first. - return &__ubsan_vptr_hash_set[V]; + return &__ubsan_vptr_hash_set[First]; } /// A cache of recently-checked hashes. Mini hash table with "random" evictions. __ubsan::HashValue -__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize] = { 1 }; +__ubsan::__ubsan_vptr_type_cache[__ubsan::VptrTypeCacheSize]; /// \brief Determine whether \p Derived has a \p Base base class subobject at /// offset \p Offset. diff --git a/libsanitizer/ubsan/ubsan_value.h b/libsanitizer/ubsan/ubsan_value.h index 9b31eb7..6ca0f56 100644 --- a/libsanitizer/ubsan/ubsan_value.h +++ b/libsanitizer/ubsan/ubsan_value.h @@ -23,8 +23,8 @@ // FIXME: Move this out to a config header. #if __SIZEOF_INT128__ -typedef __int128 s128; -typedef unsigned __int128 u128; +__extension__ typedef __int128 s128; +__extension__ typedef unsigned __int128 u128; #define HAVE_INT128_T 1 #else #define HAVE_INT128_T 0 -- cgit v1.1