diff options
author | Martin Liska <mliska@suse.cz> | 2021-05-12 14:37:22 +0200 |
---|---|---|
committer | Martin Liska <mliska@suse.cz> | 2021-05-13 09:29:17 +0200 |
commit | d0fee87e0ce24f066cde3dbf9605abce24dd75e1 (patch) | |
tree | 9172c165d55d36021fa70059ed0e9fef5324119e /libsanitizer/tsan | |
parent | 810afb0b5fbb9da1e0e51ee9607f275f14c17459 (diff) | |
download | gcc-d0fee87e0ce24f066cde3dbf9605abce24dd75e1.zip gcc-d0fee87e0ce24f066cde3dbf9605abce24dd75e1.tar.gz gcc-d0fee87e0ce24f066cde3dbf9605abce24dd75e1.tar.bz2 |
libsanitizer: merge from master
Merged revision: f58e0513dd95944b81ce7a6e7b49ba656de7d75f
Diffstat (limited to 'libsanitizer/tsan')
24 files changed, 469 insertions, 229 deletions
diff --git a/libsanitizer/tsan/tsan_clock.cpp b/libsanitizer/tsan/tsan_clock.cpp index c91b29c..8e51883 100644 --- a/libsanitizer/tsan/tsan_clock.cpp +++ b/libsanitizer/tsan/tsan_clock.cpp @@ -150,7 +150,7 @@ void ThreadClock::acquire(ClockCache *c, SyncClock *src) { bool acquired = false; for (unsigned i = 0; i < kDirtyTids; i++) { SyncClock::Dirty dirty = src->dirty_[i]; - unsigned tid = dirty.tid; + unsigned tid = dirty.tid(); if (tid != kInvalidTid) { if (clk_[tid] < dirty.epoch) { clk_[tid] = dirty.epoch; @@ -299,10 +299,10 @@ void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) { dst->tab_idx_ = cached_idx_; dst->size_ = cached_size_; dst->blocks_ = cached_blocks_; - CHECK_EQ(dst->dirty_[0].tid, kInvalidTid); + CHECK_EQ(dst->dirty_[0].tid(), kInvalidTid); // The cached clock is shared (immutable), // so this is where we store the current clock. - dst->dirty_[0].tid = tid_; + dst->dirty_[0].set_tid(tid_); dst->dirty_[0].epoch = clk_[tid_]; dst->release_store_tid_ = tid_; dst->release_store_reused_ = reused_; @@ -336,8 +336,7 @@ void ThreadClock::ReleaseStore(ClockCache *c, SyncClock *dst) { ce.reused = 0; i++; } - for (uptr i = 0; i < kDirtyTids; i++) - dst->dirty_[i].tid = kInvalidTid; + for (uptr i = 0; i < kDirtyTids; i++) dst->dirty_[i].set_tid(kInvalidTid); dst->release_store_tid_ = tid_; dst->release_store_reused_ = reused_; // Rememeber that we don't need to acquire it in future. @@ -369,10 +368,10 @@ void ThreadClock::UpdateCurrentThread(ClockCache *c, SyncClock *dst) const { // Update the threads time, but preserve 'acquired' flag. for (unsigned i = 0; i < kDirtyTids; i++) { SyncClock::Dirty *dirty = &dst->dirty_[i]; - const unsigned tid = dirty->tid; + const unsigned tid = dirty->tid(); if (tid == tid_ || tid == kInvalidTid) { CPP_STAT_INC(StatClockReleaseFast); - dirty->tid = tid_; + dirty->set_tid(tid_); dirty->epoch = clk_[tid_]; return; } @@ -393,8 +392,8 @@ bool ThreadClock::IsAlreadyAcquired(const SyncClock *src) const { return false; for (unsigned i = 0; i < kDirtyTids; i++) { SyncClock::Dirty dirty = src->dirty_[i]; - if (dirty.tid != kInvalidTid) { - if (clk_[dirty.tid] < dirty.epoch) + if (dirty.tid() != kInvalidTid) { + if (clk_[dirty.tid()] < dirty.epoch) return false; } } @@ -453,8 +452,7 @@ void SyncClock::ResetImpl() { blocks_ = 0; release_store_tid_ = kInvalidTid; release_store_reused_ = 0; - for (uptr i = 0; i < kDirtyTids; i++) - dirty_[i].tid = kInvalidTid; + for (uptr i = 0; i < kDirtyTids; i++) dirty_[i].set_tid(kInvalidTid); } void SyncClock::Resize(ClockCache *c, uptr nclk) { @@ -503,10 +501,10 @@ void SyncClock::Resize(ClockCache *c, uptr nclk) { void SyncClock::FlushDirty() { for (unsigned i = 0; i < kDirtyTids; i++) { Dirty *dirty = &dirty_[i]; - if (dirty->tid != kInvalidTid) { - CHECK_LT(dirty->tid, size_); - elem(dirty->tid).epoch = dirty->epoch; - dirty->tid = kInvalidTid; + if (dirty->tid() != kInvalidTid) { + CHECK_LT(dirty->tid(), size_); + elem(dirty->tid()).epoch = dirty->epoch; + dirty->set_tid(kInvalidTid); } } } @@ -559,7 +557,7 @@ ALWAYS_INLINE bool SyncClock::Cachable() const { if (size_ == 0) return false; for (unsigned i = 0; i < kDirtyTids; i++) { - if (dirty_[i].tid != kInvalidTid) + if (dirty_[i].tid() != kInvalidTid) return false; } return atomic_load_relaxed(ref_ptr(tab_)) == 1; @@ -606,7 +604,7 @@ ALWAYS_INLINE void SyncClock::append_block(u32 idx) { u64 SyncClock::get(unsigned tid) const { for (unsigned i = 0; i < kDirtyTids; i++) { Dirty dirty = dirty_[i]; - if (dirty.tid == tid) + if (dirty.tid() == tid) return dirty.epoch; } return elem(tid).epoch; @@ -625,9 +623,8 @@ void SyncClock::DebugDump(int(*printf)(const char *s, ...)) { for (uptr i = 0; i < size_; i++) printf("%s%llu", i == 0 ? "" : ",", elem(i).reused); printf("] release_store_tid=%d/%d dirty_tids=%d[%llu]/%d[%llu]", - release_store_tid_, release_store_reused_, - dirty_[0].tid, dirty_[0].epoch, - dirty_[1].tid, dirty_[1].epoch); + release_store_tid_, release_store_reused_, dirty_[0].tid(), + dirty_[0].epoch, dirty_[1].tid(), dirty_[1].epoch); } void SyncClock::Iter::Next() { diff --git a/libsanitizer/tsan/tsan_clock.h b/libsanitizer/tsan/tsan_clock.h index 736cdae..31376a1 100644 --- a/libsanitizer/tsan/tsan_clock.h +++ b/libsanitizer/tsan/tsan_clock.h @@ -17,7 +17,7 @@ namespace __tsan { -typedef DenseSlabAlloc<ClockBlock, 1<<16, 1<<10> ClockAlloc; +typedef DenseSlabAlloc<ClockBlock, 1 << 22, 1 << 10> ClockAlloc; typedef DenseSlabAllocCache ClockCache; // The clock that lives in sync variables (mutexes, atomics, etc). @@ -65,10 +65,20 @@ class SyncClock { static const uptr kDirtyTids = 2; struct Dirty { - u64 epoch : kClkBits; - u64 tid : 64 - kClkBits; // kInvalidId if not active + u32 tid() const { return tid_ == kShortInvalidTid ? kInvalidTid : tid_; } + void set_tid(u32 tid) { + tid_ = tid == kInvalidTid ? kShortInvalidTid : tid; + } + u64 epoch : kClkBits; + + private: + // Full kInvalidTid won't fit into Dirty::tid. + static const u64 kShortInvalidTid = (1ull << (64 - kClkBits)) - 1; + u64 tid_ : 64 - kClkBits; // kInvalidId if not active }; + static_assert(sizeof(Dirty) == 8, "Dirty is not 64bit"); + unsigned release_store_tid_; unsigned release_store_reused_; Dirty dirty_[kDirtyTids]; diff --git a/libsanitizer/tsan/tsan_defs.h b/libsanitizer/tsan/tsan_defs.h index 293d7de..f53787a 100644 --- a/libsanitizer/tsan/tsan_defs.h +++ b/libsanitizer/tsan/tsan_defs.h @@ -98,8 +98,6 @@ const bool kCollectHistory = false; const bool kCollectHistory = true; #endif -const u16 kInvalidTid = kMaxTid + 1; - // The following "build consistency" machinery ensures that all source files // are built in the same configuration. Inconsistent builds lead to // hard to debug crashes. diff --git a/libsanitizer/tsan/tsan_dense_alloc.h b/libsanitizer/tsan/tsan_dense_alloc.h index 64fc50e..6c89e40 100644 --- a/libsanitizer/tsan/tsan_dense_alloc.h +++ b/libsanitizer/tsan/tsan_dense_alloc.h @@ -29,28 +29,40 @@ class DenseSlabAllocCache { typedef u32 IndexT; uptr pos; IndexT cache[kSize]; - template<typename T, uptr kL1Size, uptr kL2Size> friend class DenseSlabAlloc; + template <typename, uptr, uptr, u64> + friend class DenseSlabAlloc; }; -template<typename T, uptr kL1Size, uptr kL2Size> +template <typename T, uptr kL1Size, uptr kL2Size, u64 kReserved = 0> class DenseSlabAlloc { public: typedef DenseSlabAllocCache Cache; typedef typename Cache::IndexT IndexT; - explicit DenseSlabAlloc(const char *name) { - // Check that kL1Size and kL2Size are sane. - CHECK_EQ(kL1Size & (kL1Size - 1), 0); - CHECK_EQ(kL2Size & (kL2Size - 1), 0); - CHECK_GE(1ull << (sizeof(IndexT) * 8), kL1Size * kL2Size); - // Check that it makes sense to use the dense alloc. - CHECK_GE(sizeof(T), sizeof(IndexT)); - internal_memset(map_, 0, sizeof(map_)); + static_assert((kL1Size & (kL1Size - 1)) == 0, + "kL1Size must be a power-of-two"); + static_assert((kL2Size & (kL2Size - 1)) == 0, + "kL2Size must be a power-of-two"); + static_assert((kL1Size * kL2Size) <= (1ull << (sizeof(IndexT) * 8)), + "kL1Size/kL2Size are too large"); + static_assert(((kL1Size * kL2Size - 1) & kReserved) == 0, + "reserved bits don't fit"); + static_assert(sizeof(T) > sizeof(IndexT), + "it doesn't make sense to use dense alloc"); + + explicit DenseSlabAlloc(LinkerInitialized, const char *name) { freelist_ = 0; fillpos_ = 0; name_ = name; } + explicit DenseSlabAlloc(const char *name) + : DenseSlabAlloc(LINKER_INITIALIZED, name) { + // It can be very large. + // Don't page it in for linker initialized objects. + internal_memset(map_, 0, sizeof(map_)); + } + ~DenseSlabAlloc() { for (uptr i = 0; i < kL1Size; i++) { if (map_[i] != 0) diff --git a/libsanitizer/tsan/tsan_external.cpp b/libsanitizer/tsan/tsan_external.cpp index 466b2bf..a87e12f 100644 --- a/libsanitizer/tsan/tsan_external.cpp +++ b/libsanitizer/tsan/tsan_external.cpp @@ -111,12 +111,12 @@ void __tsan_external_assign_tag(void *addr, void *tag) { SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_read(void *addr, void *caller_pc, void *tag) { - ExternalAccess(addr, STRIP_PC(caller_pc), tag, MemoryRead); + ExternalAccess(addr, STRIP_PAC_PC(caller_pc), tag, MemoryRead); } SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_write(void *addr, void *caller_pc, void *tag) { - ExternalAccess(addr, STRIP_PC(caller_pc), tag, MemoryWrite); + ExternalAccess(addr, STRIP_PAC_PC(caller_pc), tag, MemoryWrite); } } // extern "C" diff --git a/libsanitizer/tsan/tsan_interceptors_mac.cpp b/libsanitizer/tsan/tsan_interceptors_mac.cpp index aa29536..ed10fcc 100644 --- a/libsanitizer/tsan/tsan_interceptors_mac.cpp +++ b/libsanitizer/tsan/tsan_interceptors_mac.cpp @@ -438,6 +438,7 @@ struct fake_shared_weak_count { virtual void on_zero_shared() = 0; virtual void _unused_0x18() = 0; virtual void on_zero_shared_weak() = 0; + virtual ~fake_shared_weak_count() = 0; // suppress -Wnon-virtual-dtor }; } // namespace diff --git a/libsanitizer/tsan/tsan_interceptors_posix.cpp b/libsanitizer/tsan/tsan_interceptors_posix.cpp index aa04d8d..2651e22 100644 --- a/libsanitizer/tsan/tsan_interceptors_posix.cpp +++ b/libsanitizer/tsan/tsan_interceptors_posix.cpp @@ -54,10 +54,6 @@ using namespace __tsan; #define vfork __vfork14 #endif -#if SANITIZER_ANDROID -#define mallopt(a, b) -#endif - #ifdef __mips__ const int kSigCount = 129; #else @@ -85,6 +81,8 @@ extern "C" int pthread_attr_init(void *attr); extern "C" int pthread_attr_destroy(void *attr); DECLARE_REAL(int, pthread_attr_getdetachstate, void *, void *) extern "C" int pthread_attr_setstacksize(void *attr, uptr stacksize); +extern "C" int pthread_atfork(void (*prepare)(void), void (*parent)(void), + void (*child)(void)); extern "C" int pthread_key_create(unsigned *key, void (*destructor)(void* v)); extern "C" int pthread_setspecific(unsigned key, const void *v); DECLARE_REAL(int, pthread_mutexattr_gettype, void *, void *) @@ -97,7 +95,7 @@ extern "C" void _exit(int status); extern "C" int fileno_unlocked(void *stream); extern "C" int dirfd(void *dirp); #endif -#if !SANITIZER_FREEBSD && !SANITIZER_ANDROID && !SANITIZER_NETBSD +#if SANITIZER_GLIBC extern "C" int mallopt(int param, int value); #endif #if SANITIZER_NETBSD @@ -659,8 +657,11 @@ TSAN_INTERCEPTOR(void*, malloc, uptr size) { return p; } +// In glibc<2.25, dynamic TLS blocks are allocated by __libc_memalign. Intercept +// __libc_memalign so that (1) we can detect races (2) free will not be called +// on libc internally allocated blocks. TSAN_INTERCEPTOR(void*, __libc_memalign, uptr align, uptr sz) { - SCOPED_TSAN_INTERCEPTOR(__libc_memalign, align, sz); + SCOPED_INTERCEPTOR_RAW(__libc_memalign, align, sz); return user_memalign(thr, pc, align, sz); } @@ -773,6 +774,11 @@ static void *mmap_interceptor(ThreadState *thr, uptr pc, Mmap real_mmap, if (!fix_mmap_addr(&addr, sz, flags)) return MAP_FAILED; void *res = real_mmap(addr, sz, prot, flags, fd, off); if (res != MAP_FAILED) { + if (!IsAppMem((uptr)res) || !IsAppMem((uptr)res + sz - 1)) { + Report("ThreadSanitizer: mmap at bad address: addr=%p size=%p res=%p\n", + addr, (void*)sz, res); + Die(); + } if (fd > 0) FdAccess(thr, pc, fd); MemoryRangeImitateWriteOrResetRange(thr, pc, (uptr)res, sz); } @@ -1122,27 +1128,37 @@ static void *init_cond(void *c, bool force = false) { return (void*)cond; } +namespace { + +template <class Fn> struct CondMutexUnlockCtx { ScopedInterceptor *si; ThreadState *thr; uptr pc; void *m; + void *c; + const Fn &fn; + + int Cancel() const { return fn(); } + void Unlock() const; }; -static void cond_mutex_unlock(CondMutexUnlockCtx *arg) { +template <class Fn> +void CondMutexUnlockCtx<Fn>::Unlock() const { // pthread_cond_wait interceptor has enabled async signal delivery // (see BlockingCall below). Disable async signals since we are running // tsan code. Also ScopedInterceptor and BlockingCall destructors won't run // since the thread is cancelled, so we have to manually execute them // (the thread still can run some user code due to pthread_cleanup_push). - ThreadSignalContext *ctx = SigCtx(arg->thr); + ThreadSignalContext *ctx = SigCtx(thr); CHECK_EQ(atomic_load(&ctx->in_blocking_func, memory_order_relaxed), 1); atomic_store(&ctx->in_blocking_func, 0, memory_order_relaxed); - MutexPostLock(arg->thr, arg->pc, (uptr)arg->m, MutexFlagDoPreLockOnPostLock); + MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock); // Undo BlockingCall ctor effects. - arg->thr->ignore_interceptors--; - arg->si->~ScopedInterceptor(); + thr->ignore_interceptors--; + si->~ScopedInterceptor(); } +} // namespace INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { void *cond = init_cond(c, true); @@ -1151,20 +1167,24 @@ INTERCEPTOR(int, pthread_cond_init, void *c, void *a) { return REAL(pthread_cond_init)(cond, a); } -static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, - int (*fn)(void *c, void *m, void *abstime), void *c, - void *m, void *t) { +template <class Fn> +int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, const Fn &fn, + void *c, void *m) { MemoryAccessRange(thr, pc, (uptr)c, sizeof(uptr), false); MutexUnlock(thr, pc, (uptr)m); - CondMutexUnlockCtx arg = {si, thr, pc, m}; int res = 0; // This ensures that we handle mutex lock even in case of pthread_cancel. // See test/tsan/cond_cancel.cpp. { // Enable signal delivery while the thread is blocked. BlockingCall bc(thr); + CondMutexUnlockCtx<Fn> arg = {si, thr, pc, m, c, fn}; res = call_pthread_cancel_with_cleanup( - fn, c, m, t, (void (*)(void *arg))cond_mutex_unlock, &arg); + [](void *arg) -> int { + return ((const CondMutexUnlockCtx<Fn> *)arg)->Cancel(); + }, + [](void *arg) { ((const CondMutexUnlockCtx<Fn> *)arg)->Unlock(); }, + &arg); } if (res == errno_EOWNERDEAD) MutexRepair(thr, pc, (uptr)m); MutexPostLock(thr, pc, (uptr)m, MutexFlagDoPreLockOnPostLock); @@ -1174,25 +1194,46 @@ static int cond_wait(ThreadState *thr, uptr pc, ScopedInterceptor *si, INTERCEPTOR(int, pthread_cond_wait, void *c, void *m) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_wait, cond, m); - return cond_wait(thr, pc, &si, (int (*)(void *c, void *m, void *abstime))REAL( - pthread_cond_wait), - cond, m, 0); + return cond_wait( + thr, pc, &si, [=]() { return REAL(pthread_cond_wait)(cond, m); }, cond, + m); } INTERCEPTOR(int, pthread_cond_timedwait, void *c, void *m, void *abstime) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait, cond, m, abstime); - return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait), cond, m, - abstime); + return cond_wait( + thr, pc, &si, + [=]() { return REAL(pthread_cond_timedwait)(cond, m, abstime); }, cond, + m); } +#if SANITIZER_LINUX +INTERCEPTOR(int, pthread_cond_clockwait, void *c, void *m, + __sanitizer_clockid_t clock, void *abstime) { + void *cond = init_cond(c); + SCOPED_TSAN_INTERCEPTOR(pthread_cond_clockwait, cond, m, clock, abstime); + return cond_wait( + thr, pc, &si, + [=]() { return REAL(pthread_cond_clockwait)(cond, m, clock, abstime); }, + cond, m); +} +#define TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT TSAN_INTERCEPT(pthread_cond_clockwait) +#else +#define TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT +#endif + #if SANITIZER_MAC INTERCEPTOR(int, pthread_cond_timedwait_relative_np, void *c, void *m, void *reltime) { void *cond = init_cond(c); SCOPED_TSAN_INTERCEPTOR(pthread_cond_timedwait_relative_np, cond, m, reltime); - return cond_wait(thr, pc, &si, REAL(pthread_cond_timedwait_relative_np), cond, - m, reltime); + return cond_wait( + thr, pc, &si, + [=]() { + return REAL(pthread_cond_timedwait_relative_np)(cond, m, reltime); + }, + cond, m); } #endif @@ -1937,7 +1978,8 @@ static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire, // because in async signal processing case (when handler is called directly // from rtl_generic_sighandler) we have not yet received the reraised // signal; and it looks too fragile to intercept all ways to reraise a signal. - if (flags()->report_bugs && !sync && sig != SIGTERM && errno != 99) { + if (ShouldReport(thr, ReportTypeErrnoInSignal) && !sync && sig != SIGTERM && + errno != 99) { VarSizeStackTrace stack; // StackTrace::GetNestInstructionPc(pc) is used because return address is // expected, OutputReport() will undo this. @@ -2107,26 +2149,32 @@ TSAN_INTERCEPTOR(int, fork, int fake) { if (in_symbolizer()) return REAL(fork)(fake); SCOPED_INTERCEPTOR_RAW(fork, fake); + return REAL(fork)(fake); +} + +void atfork_prepare() { + if (in_symbolizer()) + return; + ThreadState *thr = cur_thread(); + const uptr pc = StackTrace::GetCurrentPc(); ForkBefore(thr, pc); - int pid; - { - // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and - // we'll assert in CheckNoLocks() unless we ignore interceptors. - ScopedIgnoreInterceptors ignore; - pid = REAL(fork)(fake); - } - if (pid == 0) { - // child - ForkChildAfter(thr, pc); - FdOnFork(thr, pc); - } else if (pid > 0) { - // parent - ForkParentAfter(thr, pc); - } else { - // error - ForkParentAfter(thr, pc); - } - return pid; +} + +void atfork_parent() { + if (in_symbolizer()) + return; + ThreadState *thr = cur_thread(); + const uptr pc = StackTrace::GetCurrentPc(); + ForkParentAfter(thr, pc); +} + +void atfork_child() { + if (in_symbolizer()) + return; + ThreadState *thr = cur_thread(); + const uptr pc = StackTrace::GetCurrentPc(); + ForkChildAfter(thr, pc); + FdOnFork(thr, pc); } TSAN_INTERCEPTOR(int, vfork, int fake) { @@ -2479,13 +2527,10 @@ static USED void syscall_fd_release(uptr pc, int fd) { FdRelease(thr, pc, fd); } -static void syscall_pre_fork(uptr pc) { - TSAN_SYSCALL(); - ForkBefore(thr, pc); -} +static void syscall_pre_fork(uptr pc) { ForkBefore(cur_thread(), pc); } static void syscall_post_fork(uptr pc, int pid) { - TSAN_SYSCALL(); + ThreadState *thr = cur_thread(); if (pid == 0) { // child ForkChildAfter(thr, pc); @@ -2635,7 +2680,7 @@ void InitializeInterceptors() { #endif // Instruct libc malloc to consume less memory. -#if SANITIZER_LINUX +#if SANITIZER_GLIBC mallopt(1, 0); // M_MXFAST mallopt(-3, 32*1024); // M_MMAP_THRESHOLD #endif @@ -2698,6 +2743,8 @@ void InitializeInterceptors() { TSAN_INTERCEPT_VER(pthread_cond_timedwait, PTHREAD_ABI_BASE); TSAN_INTERCEPT_VER(pthread_cond_destroy, PTHREAD_ABI_BASE); + TSAN_MAYBE_PTHREAD_COND_CLOCKWAIT; + TSAN_INTERCEPT(pthread_mutex_init); TSAN_INTERCEPT(pthread_mutex_destroy); TSAN_INTERCEPT(pthread_mutex_trylock); @@ -2799,6 +2846,10 @@ void InitializeInterceptors() { Printf("ThreadSanitizer: failed to setup atexit callback\n"); Die(); } + if (pthread_atfork(atfork_prepare, atfork_parent, atfork_child)) { + Printf("ThreadSanitizer: failed to setup atfork callbacks\n"); + Die(); + } #if !SANITIZER_MAC && !SANITIZER_NETBSD && !SANITIZER_FREEBSD if (pthread_key_create(&interceptor_ctx()->finalize_key, &thread_finalize)) { diff --git a/libsanitizer/tsan/tsan_interface.cpp b/libsanitizer/tsan/tsan_interface.cpp index 55f1c98..9bd0e85 100644 --- a/libsanitizer/tsan/tsan_interface.cpp +++ b/libsanitizer/tsan/tsan_interface.cpp @@ -40,13 +40,13 @@ void __tsan_write16(void *addr) { } void __tsan_read16_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr + 8, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr + 8, kSizeLog8); } void __tsan_write16_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr + 8, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr + 8, kSizeLog8); } // __tsan_unaligned_read/write calls are emitted by compiler. diff --git a/libsanitizer/tsan/tsan_interface.h b/libsanitizer/tsan/tsan_interface.h index 6d7286c..6e022b5 100644 --- a/libsanitizer/tsan/tsan_interface.h +++ b/libsanitizer/tsan/tsan_interface.h @@ -204,7 +204,7 @@ __extension__ typedef __int128 a128; #endif // Part of ABI, do not change. -// https://github.com/llvm/llvm-project/blob/master/libcxx/include/atomic +// https://github.com/llvm/llvm-project/blob/main/libcxx/include/atomic typedef enum { mo_relaxed, mo_consume, @@ -415,6 +415,13 @@ void __tsan_go_atomic32_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, SANITIZER_INTERFACE_ATTRIBUTE void __tsan_go_atomic64_compare_exchange(ThreadState *thr, uptr cpc, uptr pc, u8 *a); + +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_on_initialize(); + +SANITIZER_INTERFACE_ATTRIBUTE +int __tsan_on_finalize(int failed); + } // extern "C" } // namespace __tsan diff --git a/libsanitizer/tsan/tsan_interface_inl.h b/libsanitizer/tsan/tsan_interface_inl.h index f5d743c..5e77d4d 100644 --- a/libsanitizer/tsan/tsan_interface_inl.h +++ b/libsanitizer/tsan/tsan_interface_inl.h @@ -51,35 +51,35 @@ void __tsan_write8(void *addr) { } void __tsan_read1_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog1); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog1); } void __tsan_read2_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog2); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog2); } void __tsan_read4_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog4); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog4); } void __tsan_read8_pc(void *addr, void *pc) { - MemoryRead(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); + MemoryRead(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); } void __tsan_write1_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog1); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog1); } void __tsan_write2_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog2); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog2); } void __tsan_write4_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog4); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog4); } void __tsan_write8_pc(void *addr, void *pc) { - MemoryWrite(cur_thread(), STRIP_PC(pc), (uptr)addr, kSizeLog8); + MemoryWrite(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, kSizeLog8); } void __tsan_vptr_update(void **vptr_p, void *new_val) { @@ -101,7 +101,7 @@ void __tsan_vptr_read(void **vptr_p) { } void __tsan_func_entry(void *pc) { - FuncEntry(cur_thread(), STRIP_PC(pc)); + FuncEntry(cur_thread(), STRIP_PAC_PC(pc)); } void __tsan_func_exit() { @@ -125,9 +125,9 @@ void __tsan_write_range(void *addr, uptr size) { } void __tsan_read_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), STRIP_PC(pc), (uptr)addr, size, false); + MemoryAccessRange(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, size, false); } void __tsan_write_range_pc(void *addr, uptr size, void *pc) { - MemoryAccessRange(cur_thread(), STRIP_PC(pc), (uptr)addr, size, true); + MemoryAccessRange(cur_thread(), STRIP_PAC_PC(pc), (uptr)addr, size, true); } diff --git a/libsanitizer/tsan/tsan_mman.cpp b/libsanitizer/tsan/tsan_mman.cpp index 743e67bf..45a39f0 100644 --- a/libsanitizer/tsan/tsan_mman.cpp +++ b/libsanitizer/tsan/tsan_mman.cpp @@ -145,7 +145,7 @@ void AllocatorPrintStats() { static void SignalUnsafeCall(ThreadState *thr, uptr pc) { if (atomic_load_relaxed(&thr->in_signal_handler) == 0 || - !flags()->report_signal_unsafe) + !ShouldReport(thr, ReportTypeSignalUnsafe)) return; VarSizeStackTrace stack; ObtainCurrentStack(thr, pc, &stack); diff --git a/libsanitizer/tsan/tsan_platform.h b/libsanitizer/tsan/tsan_platform.h index 16169ca..101522d 100644 --- a/libsanitizer/tsan/tsan_platform.h +++ b/libsanitizer/tsan/tsan_platform.h @@ -23,9 +23,21 @@ namespace __tsan { +#if defined(__x86_64__) +#define HAS_48_BIT_ADDRESS_SPACE 1 +#elif SANITIZER_IOSSIM // arm64 iOS simulators (order of #if matters) +#define HAS_48_BIT_ADDRESS_SPACE 1 +#elif SANITIZER_IOS // arm64 iOS devices (order of #if matters) +#define HAS_48_BIT_ADDRESS_SPACE 0 +#elif SANITIZER_MAC // arm64 macOS (order of #if matters) +#define HAS_48_BIT_ADDRESS_SPACE 1 +#else +#define HAS_48_BIT_ADDRESS_SPACE 0 +#endif + #if !SANITIZER_GO -#if defined(__x86_64__) +#if HAS_48_BIT_ADDRESS_SPACE /* C/C++ on linux/x86_64 and freebsd/x86_64 0000 0000 1000 - 0080 0000 0000: main binary and/or MAP_32BIT mappings (512GB) @@ -93,7 +105,7 @@ fe00 0000 00 - ff00 0000 00: heap (4 GB) ff00 0000 00 - ff80 0000 00: - (2 GB) ff80 0000 00 - ffff ffff ff: modules and main thread stack (<2 GB) */ -struct Mapping { +struct Mapping40 { static const uptr kMetaShadowBeg = 0x4000000000ull; static const uptr kMetaShadowEnd = 0x5000000000ull; static const uptr kTraceMemBeg = 0xb000000000ull; @@ -114,6 +126,7 @@ struct Mapping { }; #define TSAN_MID_APP_RANGE 1 +#define TSAN_RUNTIME_VMA 1 #elif defined(__aarch64__) && defined(__APPLE__) /* C/C++ on Darwin/iOS/ARM64 (36-bit VMA, 64 GB VM) @@ -146,7 +159,7 @@ struct Mapping { static const uptr kVdsoBeg = 0x7000000000000000ull; }; -#elif defined(__aarch64__) +#elif defined(__aarch64__) && !defined(__APPLE__) // AArch64 supports multiple VMA which leads to multiple address transformation // functions. To support these multiple VMAS transformations and mappings TSAN // runtime for AArch64 uses an external memory read (vmaSize) to select which @@ -354,7 +367,7 @@ struct Mapping47 { #define TSAN_RUNTIME_VMA 1 #endif -#elif SANITIZER_GO && !SANITIZER_WINDOWS && defined(__x86_64__) +#elif SANITIZER_GO && !SANITIZER_WINDOWS && HAS_48_BIT_ADDRESS_SPACE /* Go on linux, darwin and freebsd on x86_64 0000 0000 1000 - 0000 1000 0000: executable @@ -502,7 +515,7 @@ Go on linux/mips64 (47-bit VMA) 6000 0000 0000 - 6200 0000 0000: traces 6200 0000 0000 - 8000 0000 0000: - */ -struct Mapping { +struct Mapping47 { static const uptr kMetaShadowBeg = 0x300000000000ull; static const uptr kMetaShadowEnd = 0x400000000000ull; static const uptr kTraceMemBeg = 0x600000000000ull; @@ -512,6 +525,9 @@ struct Mapping { static const uptr kAppMemBeg = 0x000000001000ull; static const uptr kAppMemEnd = 0x00e000000000ull; }; + +#define TSAN_RUNTIME_VMA 1 + #else # error "Unknown platform" #endif @@ -592,6 +608,16 @@ uptr MappingArchImpl(void) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return MappingImpl<Mapping40, Type>(); +#else + case 47: return MappingImpl<Mapping47, Type>(); +#endif + } + DCHECK(0); + return 0; #else return MappingImpl<Mapping, Type>(); #endif @@ -749,6 +775,16 @@ bool IsAppMem(uptr mem) { } DCHECK(0); return false; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return IsAppMemImpl<Mapping40>(mem); +#else + case 47: return IsAppMemImpl<Mapping47>(mem); +#endif + } + DCHECK(0); + return false; #else return IsAppMemImpl<Mapping>(mem); #endif @@ -780,6 +816,16 @@ bool IsShadowMem(uptr mem) { } DCHECK(0); return false; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return IsShadowMemImpl<Mapping40>(mem); +#else + case 47: return IsShadowMemImpl<Mapping47>(mem); +#endif + } + DCHECK(0); + return false; #else return IsShadowMemImpl<Mapping>(mem); #endif @@ -811,6 +857,16 @@ bool IsMetaMem(uptr mem) { } DCHECK(0); return false; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return IsMetaMemImpl<Mapping40>(mem); +#else + case 47: return IsMetaMemImpl<Mapping47>(mem); +#endif + } + DCHECK(0); + return false; #else return IsMetaMemImpl<Mapping>(mem); #endif @@ -852,6 +908,16 @@ uptr MemToShadow(uptr x) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return MemToShadowImpl<Mapping40>(x); +#else + case 47: return MemToShadowImpl<Mapping47>(x); +#endif + } + DCHECK(0); + return 0; #else return MemToShadowImpl<Mapping>(x); #endif @@ -895,6 +961,16 @@ u32 *MemToMeta(uptr x) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return MemToMetaImpl<Mapping40>(x); +#else + case 47: return MemToMetaImpl<Mapping47>(x); +#endif + } + DCHECK(0); + return 0; #else return MemToMetaImpl<Mapping>(x); #endif @@ -951,6 +1027,16 @@ uptr ShadowToMem(uptr s) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return ShadowToMemImpl<Mapping40>(s); +#else + case 47: return ShadowToMemImpl<Mapping47>(s); +#endif + } + DCHECK(0); + return 0; #else return ShadowToMemImpl<Mapping>(s); #endif @@ -990,6 +1076,16 @@ uptr GetThreadTrace(int tid) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return GetThreadTraceImpl<Mapping40>(tid); +#else + case 47: return GetThreadTraceImpl<Mapping47>(tid); +#endif + } + DCHECK(0); + return 0; #else return GetThreadTraceImpl<Mapping>(tid); #endif @@ -1024,6 +1120,16 @@ uptr GetThreadTraceHeader(int tid) { } DCHECK(0); return 0; +#elif defined(__mips64) + switch (vmaSize) { +#if !SANITIZER_GO + case 40: return GetThreadTraceHeaderImpl<Mapping40>(tid); +#else + case 47: return GetThreadTraceHeaderImpl<Mapping47>(tid); +#endif + } + DCHECK(0); + return 0; #else return GetThreadTraceHeaderImpl<Mapping>(tid); #endif @@ -1040,9 +1146,8 @@ int ExtractRecvmsgFDs(void *msg, int *fds, int nfd); uptr ExtractLongJmpSp(uptr *env); void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size); -int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, - void *abstime), void *c, void *m, void *abstime, - void(*cleanup)(void *arg), void *arg); +int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), + void (*cleanup)(void *arg), void *arg); void DestroyThreadState(); void PlatformCleanUpThreadState(ThreadState *thr); diff --git a/libsanitizer/tsan/tsan_platform_linux.cpp b/libsanitizer/tsan/tsan_platform_linux.cpp index d136dcb..e5b6690 100644 --- a/libsanitizer/tsan/tsan_platform_linux.cpp +++ b/libsanitizer/tsan/tsan_platform_linux.cpp @@ -250,6 +250,20 @@ void InitializePlatformEarly() { Die(); } # endif +#elif defined(__mips64) +# if !SANITIZER_GO + if (vmaSize != 40) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %zd - Supported 40\n", vmaSize); + Die(); + } +# else + if (vmaSize != 47) { + Printf("FATAL: ThreadSanitizer: unsupported VMA range\n"); + Printf("FATAL: Found %zd - Supported 47\n", vmaSize); + Die(); + } +# endif #endif #endif } @@ -443,14 +457,13 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) { // Note: this function runs with async signals enabled, // so it must not touch any tsan state. -int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, - void *abstime), void *c, void *m, void *abstime, - void(*cleanup)(void *arg), void *arg) { +int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), + void (*cleanup)(void *arg), void *arg) { // pthread_cleanup_push/pop are hardcore macros mess. // We can't intercept nor call them w/o including pthread.h. int res; pthread_cleanup_push(cleanup, arg); - res = fn(c, m, abstime); + res = fn(arg); pthread_cleanup_pop(0); return res; } @@ -484,7 +497,7 @@ ThreadState *cur_thread() { dead_thread_state->fast_state.SetIgnoreBit(); dead_thread_state->ignore_interceptors = 1; dead_thread_state->is_dead = true; - *const_cast<int*>(&dead_thread_state->tid) = -1; + *const_cast<u32*>(&dead_thread_state->tid) = -1; CHECK_EQ(0, internal_mprotect(dead_thread_state, sizeof(ThreadState), PROT_READ)); } diff --git a/libsanitizer/tsan/tsan_platform_mac.cpp b/libsanitizer/tsan/tsan_platform_mac.cpp index ec2c5fb..d9719a1 100644 --- a/libsanitizer/tsan/tsan_platform_mac.cpp +++ b/libsanitizer/tsan/tsan_platform_mac.cpp @@ -234,7 +234,7 @@ static void my_pthread_introspection_hook(unsigned int event, pthread_t thread, #endif void InitializePlatformEarly() { -#if !SANITIZER_GO && defined(__aarch64__) +#if !SANITIZER_GO && !HAS_48_BIT_ADDRESS_SPACE uptr max_vm = GetMaxUserVirtualAddress() + 1; if (max_vm != Mapping::kHiAppMemEnd) { Printf("ThreadSanitizer: unsupported vm address limit %p, expected %p.\n", @@ -306,14 +306,13 @@ void ImitateTlsWrite(ThreadState *thr, uptr tls_addr, uptr tls_size) { #if !SANITIZER_GO // Note: this function runs with async signals enabled, // so it must not touch any tsan state. -int call_pthread_cancel_with_cleanup(int(*fn)(void *c, void *m, - void *abstime), void *c, void *m, void *abstime, - void(*cleanup)(void *arg), void *arg) { +int call_pthread_cancel_with_cleanup(int (*fn)(void *arg), + void (*cleanup)(void *arg), void *arg) { // pthread_cleanup_push/pop are hardcore macros mess. // We can't intercept nor call them w/o including pthread.h. int res; pthread_cleanup_push(cleanup, arg); - res = fn(c, m, abstime); + res = fn(arg); pthread_cleanup_pop(0); return res; } diff --git a/libsanitizer/tsan/tsan_platform_posix.cpp b/libsanitizer/tsan/tsan_platform_posix.cpp index d56b6c3..73e1d45 100644 --- a/libsanitizer/tsan/tsan_platform_posix.cpp +++ b/libsanitizer/tsan/tsan_platform_posix.cpp @@ -99,7 +99,7 @@ void CheckAndProtect() { Die(); } -#if defined(__aarch64__) && defined(__APPLE__) +#if defined(__aarch64__) && defined(__APPLE__) && !HAS_48_BIT_ADDRESS_SPACE ProtectRange(HeapMemEnd(), ShadowBeg()); ProtectRange(ShadowEnd(), MetaShadowBeg()); ProtectRange(MetaShadowEnd(), TraceMemBeg()); diff --git a/libsanitizer/tsan/tsan_report.cpp b/libsanitizer/tsan/tsan_report.cpp index 968c7b9..8ef9f0c 100644 --- a/libsanitizer/tsan/tsan_report.cpp +++ b/libsanitizer/tsan/tsan_report.cpp @@ -69,7 +69,7 @@ ReportDesc::~ReportDesc() { const int kThreadBufSize = 32; const char *thread_name(char *buf, int tid) { - if (tid == 0) + if (tid == kMainTid) return "main thread"; internal_snprintf(buf, kThreadBufSize, "thread T%d", tid); return buf; @@ -127,7 +127,7 @@ void PrintStack(const ReportStack *ent) { } SymbolizedStack *frame = ent->frames; for (int i = 0; frame && frame->info.address; frame = frame->next, i++) { - InternalScopedString res(2 * GetPageSizeCached()); + InternalScopedString res; RenderFrame(&res, common_flags()->stack_trace_format, i, frame->info.address, &frame->info, common_flags()->symbolize_vs_style, @@ -250,7 +250,7 @@ static void PrintMutex(const ReportMutex *rm) { static void PrintThread(const ReportThread *rt) { Decorator d; - if (rt->id == 0) // Little sense in describing the main thread. + if (rt->id == kMainTid) // Little sense in describing the main thread. return; Printf("%s", d.ThreadDescription()); Printf(" Thread T%d", rt->id); @@ -394,7 +394,7 @@ void PrintReport(const ReportDesc *rep) { #else // #if !SANITIZER_GO -const int kMainThreadId = 1; +const u32 kMainGoroutineId = 1; void PrintStack(const ReportStack *ent) { if (ent == 0 || ent->frames == 0) { @@ -415,7 +415,7 @@ static void PrintMop(const ReportMop *mop, bool first) { Printf("%s at %p by ", (first ? (mop->write ? "Write" : "Read") : (mop->write ? "Previous write" : "Previous read")), mop->addr); - if (mop->tid == kMainThreadId) + if (mop->tid == kMainGoroutineId) Printf("main goroutine:\n"); else Printf("goroutine %d:\n", mop->tid); @@ -428,7 +428,7 @@ static void PrintLocation(const ReportLocation *loc) { Printf("\n"); Printf("Heap block of size %zu at %p allocated by ", loc->heap_chunk_size, loc->heap_chunk_start); - if (loc->tid == kMainThreadId) + if (loc->tid == kMainGoroutineId) Printf("main goroutine:\n"); else Printf("goroutine %d:\n", loc->tid); @@ -448,7 +448,7 @@ static void PrintLocation(const ReportLocation *loc) { } static void PrintThread(const ReportThread *rt) { - if (rt->id == kMainThreadId) + if (rt->id == kMainGoroutineId) return; Printf("\n"); Printf("Goroutine %d (%s) created at:\n", diff --git a/libsanitizer/tsan/tsan_rtl.cpp b/libsanitizer/tsan/tsan_rtl.cpp index 3d721eb..0efa997 100644 --- a/libsanitizer/tsan/tsan_rtl.cpp +++ b/libsanitizer/tsan/tsan_rtl.cpp @@ -11,17 +11,19 @@ // Main file (entry points) for the TSan run-time. //===----------------------------------------------------------------------===// +#include "tsan_rtl.h" + #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_file.h" #include "sanitizer_common/sanitizer_libc.h" -#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_placement_new.h" +#include "sanitizer_common/sanitizer_stackdepot.h" #include "sanitizer_common/sanitizer_symbolizer.h" #include "tsan_defs.h" -#include "tsan_platform.h" -#include "tsan_rtl.h" +#include "tsan_interface.h" #include "tsan_mman.h" +#include "tsan_platform.h" #include "tsan_suppressions.h" #include "tsan_symbolize.h" #include "ubsan/ubsan_init.h" @@ -56,12 +58,23 @@ Context *ctx; bool OnFinalize(bool failed); void OnInitialize(); #else +#include <dlfcn.h> SANITIZER_WEAK_CXX_DEFAULT_IMPL bool OnFinalize(bool failed) { +#if !SANITIZER_GO + if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_finalize")) + return reinterpret_cast<decltype(&__tsan_on_finalize)>(ptr)(failed); +#endif return failed; } SANITIZER_WEAK_CXX_DEFAULT_IMPL -void OnInitialize() {} +void OnInitialize() { +#if !SANITIZER_GO + if (auto *ptr = dlsym(RTLD_DEFAULT, "__tsan_on_initialize")) { + return reinterpret_cast<decltype(&__tsan_on_initialize)>(ptr)(); + } +#endif +} #endif static char thread_registry_placeholder[sizeof(ThreadRegistry)]; @@ -77,12 +90,19 @@ static ThreadContextBase *CreateThreadContext(u32 tid) { new((void*)hdr) Trace(); // We are going to use only a small part of the trace with the default // value of history_size. However, the constructor writes to the whole trace. - // Unmap the unused part. + // Release the unused part. uptr hdr_end = hdr + sizeof(Trace); hdr_end -= sizeof(TraceHeader) * (kTraceParts - TraceParts()); hdr_end = RoundUp(hdr_end, GetPageSizeCached()); - if (hdr_end < hdr + sizeof(Trace)) - UnmapOrDie((void*)hdr_end, hdr + sizeof(Trace) - hdr_end); + if (hdr_end < hdr + sizeof(Trace)) { + ReleaseMemoryPagesToOS(hdr_end, hdr + sizeof(Trace)); + uptr unused = hdr + sizeof(Trace) - hdr_end; + if (hdr_end != (uptr)MmapFixedNoAccess(hdr_end, unused)) { + Report("ThreadSanitizer: failed to mprotect(%p, %p)\n", + hdr_end, unused); + CHECK("unable to mprotect" && 0); + } + } void *mem = internal_alloc(MBlockThreadContex, sizeof(ThreadContext)); return new(mem) ThreadContext(tid); } @@ -94,42 +114,45 @@ static const u32 kThreadQuarantineSize = 64; #endif Context::Context() - : initialized() - , report_mtx(MutexTypeReport, StatMtxReport) - , nreported() - , nmissed_expected() - , thread_registry(new(thread_registry_placeholder) ThreadRegistry( - CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse)) - , racy_mtx(MutexTypeRacy, StatMtxRacy) - , racy_stacks() - , racy_addresses() - , fired_suppressions_mtx(MutexTypeFired, StatMtxFired) - , clock_alloc("clock allocator") { + : initialized(), + report_mtx(MutexTypeReport, StatMtxReport), + nreported(), + nmissed_expected(), + thread_registry(new (thread_registry_placeholder) ThreadRegistry( + CreateThreadContext, kMaxTid, kThreadQuarantineSize, kMaxTidReuse)), + racy_mtx(MutexTypeRacy, StatMtxRacy), + racy_stacks(), + racy_addresses(), + fired_suppressions_mtx(MutexTypeFired, StatMtxFired), + clock_alloc(LINKER_INITIALIZED, "clock allocator") { fired_suppressions.reserve(8); } // The objects are allocated in TLS, so one may rely on zero-initialization. -ThreadState::ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, - unsigned reuse_count, - uptr stk_addr, uptr stk_size, +ThreadState::ThreadState(Context *ctx, u32 tid, int unique_id, u64 epoch, + unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size) - : fast_state(tid, epoch) - // Do not touch these, rely on zero initialization, - // they may be accessed before the ctor. - // , ignore_reads_and_writes() - // , ignore_interceptors() - , clock(tid, reuse_count) + : fast_state(tid, epoch) + // Do not touch these, rely on zero initialization, + // they may be accessed before the ctor. + // , ignore_reads_and_writes() + // , ignore_interceptors() + , + clock(tid, reuse_count) #if !SANITIZER_GO - , jmp_bufs() + , + jmp_bufs() #endif - , tid(tid) - , unique_id(unique_id) - , stk_addr(stk_addr) - , stk_size(stk_size) - , tls_addr(tls_addr) - , tls_size(tls_size) + , + tid(tid), + unique_id(unique_id), + stk_addr(stk_addr), + stk_size(stk_size), + tls_addr(tls_addr), + tls_size(tls_size) #if !SANITIZER_GO - , last_sleep_clock(tid) + , + last_sleep_clock(tid) #endif { } @@ -160,12 +183,12 @@ static void *BackgroundThread(void *arg) { } else if (internal_strcmp(flags()->profile_memory, "stderr") == 0) { mprof_fd = 2; } else { - InternalScopedString filename(kMaxPathLength); + InternalScopedString filename; filename.append("%s.%d", flags()->profile_memory, (int)internal_getpid()); fd_t fd = OpenFile(filename.data(), WrOnly); if (fd == kInvalidFd) { Printf("ThreadSanitizer: failed to open memory profile file '%s'\n", - &filename[0]); + filename.data()); } else { mprof_fd = fd; } @@ -351,6 +374,18 @@ static void TsanOnDeadlySignal(int signo, void *siginfo, void *context) { } #endif +void CheckUnwind() { + // There is high probability that interceptors will check-fail as well, + // on the other hand there is no sense in processing interceptors + // since we are going to die soon. + ScopedIgnoreInterceptors ignore; +#if !SANITIZER_GO + cur_thread()->ignore_sync++; + cur_thread()->ignore_reads_and_writes++; +#endif + PrintCurrentStackSlow(StackTrace::GetCurrentPc()); +} + void Initialize(ThreadState *thr) { // Thread safe because done before all threads exist. static bool is_initialized = false; @@ -361,7 +396,7 @@ void Initialize(ThreadState *thr) { ScopedIgnoreInterceptors ignore; SanitizerToolName = "ThreadSanitizer"; // Install tool-specific callbacks in sanitizer_common. - SetCheckFailedCallback(TsanCheckFailed); + SetCheckUnwindCallback(CheckUnwind); ctx = new(ctx_placeholder) Context; const char *env_name = SANITIZER_GO ? "GORACE" : "TSAN_OPTIONS"; @@ -499,23 +534,27 @@ int Finalize(ThreadState *thr) { void ForkBefore(ThreadState *thr, uptr pc) { ctx->thread_registry->Lock(); ctx->report_mtx.Lock(); - // Ignore memory accesses in the pthread_atfork callbacks. - // If any of them triggers a data race we will deadlock - // on the report_mtx. - // We could ignore interceptors and sync operations as well, + // Suppress all reports in the pthread_atfork callbacks. + // Reports will deadlock on the report_mtx. + // We could ignore sync operations as well, // but so far it's unclear if it will do more good or harm. // Unnecessarily ignoring things can lead to false positives later. - ThreadIgnoreBegin(thr, pc); + thr->suppress_reports++; + // On OS X, REAL(fork) can call intercepted functions (OSSpinLockLock), and + // we'll assert in CheckNoLocks() unless we ignore interceptors. + thr->ignore_interceptors++; } void ForkParentAfter(ThreadState *thr, uptr pc) { - ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore. + thr->suppress_reports--; // Enabled in ForkBefore. + thr->ignore_interceptors--; ctx->report_mtx.Unlock(); ctx->thread_registry->Unlock(); } void ForkChildAfter(ThreadState *thr, uptr pc) { - ThreadIgnoreEnd(thr, pc); // Begin is in ForkBefore. + thr->suppress_reports--; // Enabled in ForkBefore. + thr->ignore_interceptors--; ctx->report_mtx.Unlock(); ctx->thread_registry->Unlock(); diff --git a/libsanitizer/tsan/tsan_rtl.h b/libsanitizer/tsan/tsan_rtl.h index 04d474e..3ae519d 100644 --- a/libsanitizer/tsan/tsan_rtl.h +++ b/libsanitizer/tsan/tsan_rtl.h @@ -84,9 +84,6 @@ typedef Allocator::AllocatorCache AllocatorCache; Allocator *allocator(); #endif -void TsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2); - const u64 kShadowRodata = (u64)-1; // .rodata shadow marker // FastState (from most significant bit): @@ -406,7 +403,7 @@ struct ThreadState { #if TSAN_COLLECT_STATS u64 stat[StatCnt]; #endif - const int tid; + const u32 tid; const int unique_id; bool in_symbolizer; bool in_ignored_lib; @@ -447,9 +444,8 @@ struct ThreadState { const ReportDesc *current_report; - explicit ThreadState(Context *ctx, int tid, int unique_id, u64 epoch, - unsigned reuse_count, - uptr stk_addr, uptr stk_size, + explicit ThreadState(Context *ctx, u32 tid, int unique_id, u64 epoch, + unsigned reuse_count, uptr stk_addr, uptr stk_size, uptr tls_addr, uptr tls_size); }; @@ -624,6 +620,7 @@ class ScopedReport : public ScopedReportBase { ScopedErrorReportLock lock_; }; +bool ShouldReport(ThreadState *thr, ReportType typ); ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack); void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, MutexSet *mset, uptr *tag = nullptr); diff --git a/libsanitizer/tsan/tsan_rtl_mutex.cpp b/libsanitizer/tsan/tsan_rtl_mutex.cpp index 27897f0..0a8f3aa 100644 --- a/libsanitizer/tsan/tsan_rtl_mutex.cpp +++ b/libsanitizer/tsan/tsan_rtl_mutex.cpp @@ -51,6 +51,8 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ, // or false positives (e.g. unlock in a different thread). if (SANITIZER_GO) return; + if (!ShouldReport(thr, typ)) + return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(typ); rep.AddMutex(mid); @@ -96,9 +98,8 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { ctx->dd->MutexInit(&cb, &s->dd); } bool unlock_locked = false; - if (flags()->report_destroy_locked - && s->owner_tid != SyncVar::kInvalidTid - && !s->IsFlagSet(MutexFlagBroken)) { + if (flags()->report_destroy_locked && s->owner_tid != kInvalidTid && + !s->IsFlagSet(MutexFlagBroken)) { s->SetFlags(MutexFlagBroken); unlock_locked = true; } @@ -107,7 +108,7 @@ void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { if (!unlock_locked) s->Reset(thr->proc()); // must not reset it before the report is printed s->mtx.Unlock(); - if (unlock_locked) { + if (unlock_locked && ShouldReport(thr, ReportTypeMutexDestroyLocked)) { ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeMutexDestroyLocked); rep.AddMutex(mid); @@ -169,7 +170,7 @@ void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeLock, s->GetId()); bool report_double_lock = false; - if (s->owner_tid == SyncVar::kInvalidTid) { + if (s->owner_tid == kInvalidTid) { CHECK_EQ(s->recursion, 0); s->owner_tid = thr->tid; s->last_lock = thr->fast_state.raw(); @@ -229,7 +230,7 @@ int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { s->recursion -= rec; if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); - s->owner_tid = SyncVar::kInvalidTid; + s->owner_tid = kInvalidTid; ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); @@ -275,7 +276,7 @@ void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRLock, s->GetId()); bool report_bad_lock = false; - if (s->owner_tid != SyncVar::kInvalidTid) { + if (s->owner_tid != kInvalidTid) { if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { s->SetFlags(MutexFlagBroken); report_bad_lock = true; @@ -314,7 +315,7 @@ void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) { thr->fast_state.IncrementEpoch(); TraceAddEvent(thr, thr->fast_state, EventTypeRUnlock, s->GetId()); bool report_bad_unlock = false; - if (s->owner_tid != SyncVar::kInvalidTid) { + if (s->owner_tid != kInvalidTid) { if (flags()->report_mutex_bugs && !s->IsFlagSet(MutexFlagBroken)) { s->SetFlags(MutexFlagBroken); report_bad_unlock = true; @@ -344,7 +345,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); bool write = true; bool report_bad_unlock = false; - if (s->owner_tid == SyncVar::kInvalidTid) { + if (s->owner_tid == kInvalidTid) { // Seems to be read unlock. write = false; StatInc(thr, StatMutexReadUnlock); @@ -359,7 +360,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { s->recursion--; if (s->recursion == 0) { StatInc(thr, StatMutexUnlock); - s->owner_tid = SyncVar::kInvalidTid; + s->owner_tid = kInvalidTid; ReleaseStoreImpl(thr, pc, &s->clock); } else { StatInc(thr, StatMutexRecUnlock); @@ -387,7 +388,7 @@ void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) { void MutexRepair(ThreadState *thr, uptr pc, uptr addr) { DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr); SyncVar *s = ctx->metamap.GetOrCreateAndLock(thr, pc, addr, true); - s->owner_tid = SyncVar::kInvalidTid; + s->owner_tid = kInvalidTid; s->recursion = 0; s->mtx.Unlock(); } @@ -534,7 +535,7 @@ void AcquireReleaseImpl(ThreadState *thr, uptr pc, SyncClock *c) { } void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { - if (r == 0) + if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock)) return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeDeadlock); diff --git a/libsanitizer/tsan/tsan_rtl_ppc64.S b/libsanitizer/tsan/tsan_rtl_ppc64.S index 9e533a7..8285e21 100644 --- a/libsanitizer/tsan/tsan_rtl_ppc64.S +++ b/libsanitizer/tsan/tsan_rtl_ppc64.S @@ -1,6 +1,5 @@ #include "tsan_ppc_regs.h" - .machine altivec .section .text .hidden __tsan_setjmp .globl _setjmp diff --git a/libsanitizer/tsan/tsan_rtl_report.cpp b/libsanitizer/tsan/tsan_rtl_report.cpp index 208d0df..706794f 100644 --- a/libsanitizer/tsan/tsan_rtl_report.cpp +++ b/libsanitizer/tsan/tsan_rtl_report.cpp @@ -31,23 +31,6 @@ using namespace __sanitizer; static ReportStack *SymbolizeStack(StackTrace trace); -void TsanCheckFailed(const char *file, int line, const char *cond, - u64 v1, u64 v2) { - // There is high probability that interceptors will check-fail as well, - // on the other hand there is no sense in processing interceptors - // since we are going to die soon. - ScopedIgnoreInterceptors ignore; -#if !SANITIZER_GO - cur_thread()->ignore_sync++; - cur_thread()->ignore_reads_and_writes++; -#endif - Printf("FATAL: ThreadSanitizer CHECK failed: " - "%s:%d \"%s\" (0x%zx, 0x%zx)\n", - file, line, cond, (uptr)v1, (uptr)v2); - PrintCurrentStackSlow(StackTrace::GetCurrentPc()); - Die(); -} - // Can be overriden by an application/test to intercept reports. #ifdef TSAN_EXTERNAL_HOOKS bool OnReport(const ReportDesc *rep, bool suppressed); @@ -142,6 +125,34 @@ static ReportStack *SymbolizeStack(StackTrace trace) { return stack; } +bool ShouldReport(ThreadState *thr, ReportType typ) { + // We set thr->suppress_reports in the fork context. + // Taking any locking in the fork context can lead to deadlocks. + // If any locks are already taken, it's too late to do this check. + CheckNoLocks(thr); + // For the same reason check we didn't lock thread_registry yet. + if (SANITIZER_DEBUG) + ThreadRegistryLock l(ctx->thread_registry); + if (!flags()->report_bugs || thr->suppress_reports) + return false; + switch (typ) { + case ReportTypeSignalUnsafe: + return flags()->report_signal_unsafe; + case ReportTypeThreadLeak: +#if !SANITIZER_GO + // It's impossible to join phantom threads + // in the child after fork. + if (ctx->after_multithreaded_fork) + return false; +#endif + return flags()->report_thread_leaks; + case ReportTypeMutexDestroyLocked: + return flags()->report_destroy_locked; + default: + return true; + } +} + ScopedReportBase::ScopedReportBase(ReportType typ, uptr tag) { ctx->thread_registry->CheckLocked(); void *mem = internal_alloc(MBlockReport, sizeof(ReportDesc)); @@ -497,8 +508,10 @@ static bool HandleRacyAddress(ThreadState *thr, uptr addr_min, uptr addr_max) { } bool OutputReport(ThreadState *thr, const ScopedReport &srep) { - if (!flags()->report_bugs || thr->suppress_reports) - return false; + // These should have been checked in ShouldReport. + // It's too late to check them here, we have already taken locks. + CHECK(flags()->report_bugs); + CHECK(!thr->suppress_reports); atomic_store_relaxed(&ctx->last_symbolize_time_ns, NanoTime()); const ReportDesc *rep = srep.GetReport(); CHECK_EQ(thr->current_report, nullptr); @@ -589,7 +602,7 @@ void ReportRace(ThreadState *thr) { // at best it will cause deadlocks on internal mutexes. ScopedIgnoreInterceptors ignore; - if (!flags()->report_bugs) + if (!ShouldReport(thr, ReportTypeRace)) return; if (!flags()->report_atomic_races && !RaceBetweenAtomicAndFree(thr)) return; @@ -722,8 +735,7 @@ void PrintCurrentStack(ThreadState *thr, uptr pc) { // However, this solution is not reliable enough, please see dvyukov's comment // http://reviews.llvm.org/D19148#406208 // Also see PR27280 comment 2 and 3 for breaking examples and analysis. -ALWAYS_INLINE -void PrintCurrentStackSlow(uptr pc) { +ALWAYS_INLINE USED void PrintCurrentStackSlow(uptr pc) { #if !SANITIZER_GO uptr bp = GET_CURRENT_FRAME(); BufferedStackTrace *ptrace = diff --git a/libsanitizer/tsan/tsan_rtl_thread.cpp b/libsanitizer/tsan/tsan_rtl_thread.cpp index d801467..6d1ccd8 100644 --- a/libsanitizer/tsan/tsan_rtl_thread.cpp +++ b/libsanitizer/tsan/tsan_rtl_thread.cpp @@ -51,7 +51,7 @@ struct OnCreatedArgs { void ThreadContext::OnCreated(void *arg) { thr = 0; - if (tid == 0) + if (tid == kMainTid) return; OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg); if (!args->thr) // GCD workers don't have a parent thread. @@ -179,7 +179,7 @@ static void MaybeReportThreadLeak(ThreadContextBase *tctx_base, void *arg) { #if !SANITIZER_GO static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { - if (tctx->tid == 0) { + if (tctx->tid == kMainTid) { Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); } else { Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled," @@ -210,7 +210,7 @@ static void ThreadCheckIgnore(ThreadState *thr) {} void ThreadFinalize(ThreadState *thr) { ThreadCheckIgnore(thr); #if !SANITIZER_GO - if (!flags()->report_thread_leaks) + if (!ShouldReport(thr, ReportTypeThreadLeak)) return; ThreadRegistryLock l(ctx->thread_registry); Vector<ThreadLeak> leaks; @@ -250,9 +250,10 @@ void ThreadStart(ThreadState *thr, int tid, tid_t os_id, uptr tls_size = 0; #if !SANITIZER_GO if (thread_type != ThreadType::Fiber) - GetThreadStackAndTls(tid == 0, &stk_addr, &stk_size, &tls_addr, &tls_size); + GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr, + &tls_size); - if (tid) { + if (tid != kMainTid) { if (stk_addr && stk_size) MemoryRangeImitateWrite(thr, /*pc=*/ 1, stk_addr, stk_size); @@ -313,7 +314,7 @@ static bool ConsumeThreadByUid(ThreadContextBase *tctx, void *arg) { int ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) { ConsumeThreadContext findCtx = {uid, nullptr}; ctx->thread_registry->FindThread(ConsumeThreadByUid, &findCtx); - int tid = findCtx.tctx ? findCtx.tctx->tid : ThreadRegistry::kUnknownTid; + int tid = findCtx.tctx ? findCtx.tctx->tid : kInvalidTid; DPrintf("#%d: ThreadTid uid=%zu tid=%d\n", thr->tid, uid, tid); return tid; } diff --git a/libsanitizer/tsan/tsan_sync.cpp b/libsanitizer/tsan/tsan_sync.cpp index 17ddd50f..ba24f98 100644 --- a/libsanitizer/tsan/tsan_sync.cpp +++ b/libsanitizer/tsan/tsan_sync.cpp @@ -53,8 +53,8 @@ void SyncVar::Reset(Processor *proc) { } MetaMap::MetaMap() - : block_alloc_("heap block allocator") - , sync_alloc_("sync allocator") { + : block_alloc_(LINKER_INITIALIZED, "heap block allocator"), + sync_alloc_(LINKER_INITIALIZED, "sync allocator") { atomic_store(&uid_gen_, 0, memory_order_relaxed); } diff --git a/libsanitizer/tsan/tsan_sync.h b/libsanitizer/tsan/tsan_sync.h index 47f2739..c4056f6 100644 --- a/libsanitizer/tsan/tsan_sync.h +++ b/libsanitizer/tsan/tsan_sync.h @@ -50,13 +50,11 @@ enum MutexFlags { struct SyncVar { SyncVar(); - static const int kInvalidTid = -1; - uptr addr; // overwritten by DenseSlabAlloc freelist Mutex mtx; u64 uid; // Globally unique id. u32 creation_stack_id; - int owner_tid; // Set only by exclusive owners. + u32 owner_tid; // Set only by exclusive owners. u64 last_lock; int recursion; atomic_uint32_t flags; @@ -130,8 +128,8 @@ class MetaMap { static const u32 kFlagMask = 3u << 30; static const u32 kFlagBlock = 1u << 30; static const u32 kFlagSync = 2u << 30; - typedef DenseSlabAlloc<MBlock, 1<<16, 1<<12> BlockAlloc; - typedef DenseSlabAlloc<SyncVar, 1<<16, 1<<10> SyncAlloc; + typedef DenseSlabAlloc<MBlock, 1 << 18, 1 << 12, kFlagMask> BlockAlloc; + typedef DenseSlabAlloc<SyncVar, 1 << 20, 1 << 10, kFlagMask> SyncAlloc; BlockAlloc block_alloc_; SyncAlloc sync_alloc_; atomic_uint64_t uid_gen_; |