diff options
Diffstat (limited to 'lib/tsan')
| -rwxr-xr-x | lib/tsan/go/buildgo.sh | 1 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_external.cc | 29 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_interface_ann.cc | 2 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_platform_linux.cc | 24 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_report.cc | 2 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_rtl.cc | 5 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_rtl.h | 36 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_rtl_mutex.cc | 4 | ||||
| -rw-r--r-- | lib/tsan/rtl/tsan_rtl_report.cc | 16 | ||||
| -rw-r--r-- | lib/tsan/tests/rtl/tsan_posix.cc | 5 |
10 files changed, 76 insertions, 48 deletions
diff --git a/lib/tsan/go/buildgo.sh b/lib/tsan/go/buildgo.sh index 42d479064c49..59176809eae0 100755 --- a/lib/tsan/go/buildgo.sh +++ b/lib/tsan/go/buildgo.sh @@ -5,6 +5,7 @@ set -e SRCS=" tsan_go.cc ../rtl/tsan_clock.cc + ../rtl/tsan_external.cc ../rtl/tsan_flags.cc ../rtl/tsan_interface_atomic.cc ../rtl/tsan_md5.cc diff --git a/lib/tsan/rtl/tsan_external.cc b/lib/tsan/rtl/tsan_external.cc index 88468e406651..2d32b6dac75a 100644 --- a/lib/tsan/rtl/tsan_external.cc +++ b/lib/tsan/rtl/tsan_external.cc @@ -17,11 +17,8 @@ namespace __tsan { #define CALLERPC ((uptr)__builtin_return_address(0)) -const uptr kMaxTag = 128; // Limited to 65,536, since MBlock only stores tags - // as 16-bit values, see tsan_defs.h. - -const char *registered_tags[kMaxTag]; -static atomic_uint32_t used_tags{1}; // Tag 0 means "no tag". NOLINT +const char *registered_tags[kExternalTagMax]; +static atomic_uint32_t used_tags{kExternalTagFirstUserAvailable}; // NOLINT. const char *GetObjectTypeFromTag(uptr tag) { if (tag == 0) return nullptr; @@ -30,25 +27,39 @@ const char *GetObjectTypeFromTag(uptr tag) { return registered_tags[tag]; } +void InsertShadowStackFrameForTag(ThreadState *thr, uptr tag) { + FuncEntry(thr, (uptr)®istered_tags[tag]); +} + +uptr TagFromShadowStackFrame(uptr pc) { + uptr tag_count = atomic_load(&used_tags, memory_order_relaxed); + void *pc_ptr = (void *)pc; + if (pc_ptr < ®istered_tags[0] || pc_ptr >= ®istered_tags[tag_count]) + return 0; + return (const char **)pc_ptr - ®istered_tags[0]; +} + +#if !SANITIZER_GO + typedef void(*AccessFunc)(ThreadState *, uptr, uptr, int); void ExternalAccess(void *addr, void *caller_pc, void *tag, AccessFunc access) { CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); ThreadState *thr = cur_thread(); - thr->external_tag = (uptr)tag; if (caller_pc) FuncEntry(thr, (uptr)caller_pc); + InsertShadowStackFrameForTag(thr, (uptr)tag); bool in_ignored_lib; if (!caller_pc || !libignore()->IsIgnored((uptr)caller_pc, &in_ignored_lib)) { access(thr, CALLERPC, (uptr)addr, kSizeLog1); } + FuncExit(thr); if (caller_pc) FuncExit(thr); - thr->external_tag = 0; } extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void *__tsan_external_register_tag(const char *object_type) { uptr new_tag = atomic_fetch_add(&used_tags, 1, memory_order_relaxed); - CHECK_LT(new_tag, kMaxTag); + CHECK_LT(new_tag, kExternalTagMax); registered_tags[new_tag] = internal_strdup(object_type); return (void *)new_tag; } @@ -78,4 +89,6 @@ void __tsan_external_write(void *addr, void *caller_pc, void *tag) { } } // extern "C" +#endif // !SANITIZER_GO + } // namespace __tsan diff --git a/lib/tsan/rtl/tsan_interface_ann.cc b/lib/tsan/rtl/tsan_interface_ann.cc index 810c84025f23..45ec45bbdbbe 100644 --- a/lib/tsan/rtl/tsan_interface_ann.cc +++ b/lib/tsan/rtl/tsan_interface_ann.cc @@ -471,7 +471,7 @@ void __tsan_mutex_create(void *m, unsigned flagz) { INTERFACE_ATTRIBUTE void __tsan_mutex_destroy(void *m, unsigned flagz) { SCOPED_ANNOTATION(__tsan_mutex_destroy); - MutexDestroy(thr, pc, (uptr)m); + MutexDestroy(thr, pc, (uptr)m, flagz); } INTERFACE_ATTRIBUTE diff --git a/lib/tsan/rtl/tsan_platform_linux.cc b/lib/tsan/rtl/tsan_platform_linux.cc index 3313288a728a..2d488cadd4fe 100644 --- a/lib/tsan/rtl/tsan_platform_linux.cc +++ b/lib/tsan/rtl/tsan_platform_linux.cc @@ -341,36 +341,22 @@ void ReplaceSystemMalloc() { } #if !SANITIZER_GO #if SANITIZER_ANDROID - -#if defined(__aarch64__) -# define __get_tls() \ - ({ void** __val; __asm__("mrs %0, tpidr_el0" : "=r"(__val)); __val; }) -#elif defined(__x86_64__) -# define __get_tls() \ - ({ void** __val; __asm__("mov %%fs:0, %0" : "=r"(__val)); __val; }) -#else -#error unsupported architecture -#endif - -// On Android, __thread is not supported. So we store the pointer to ThreadState -// in TLS_SLOT_TSAN, which is the tls slot allocated by Android bionic for tsan. -static const int TLS_SLOT_TSAN = 8; // On Android, one thread can call intercepted functions after // DestroyThreadState(), so add a fake thread state for "dead" threads. static ThreadState *dead_thread_state = nullptr; ThreadState *cur_thread() { - ThreadState* thr = (ThreadState*)__get_tls()[TLS_SLOT_TSAN]; + ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr()); if (thr == nullptr) { __sanitizer_sigset_t emptyset; internal_sigfillset(&emptyset); __sanitizer_sigset_t oldset; CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset)); - thr = reinterpret_cast<ThreadState*>(__get_tls()[TLS_SLOT_TSAN]); + thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr()); if (thr == nullptr) { thr = reinterpret_cast<ThreadState*>(MmapOrDie(sizeof(ThreadState), "ThreadState")); - __get_tls()[TLS_SLOT_TSAN] = thr; + *get_android_tls_ptr() = reinterpret_cast<uptr>(thr); if (dead_thread_state == nullptr) { dead_thread_state = reinterpret_cast<ThreadState*>( MmapOrDie(sizeof(ThreadState), "ThreadState")); @@ -392,9 +378,9 @@ void cur_thread_finalize() { internal_sigfillset(&emptyset); __sanitizer_sigset_t oldset; CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &emptyset, &oldset)); - ThreadState* thr = (ThreadState*)__get_tls()[TLS_SLOT_TSAN]; + ThreadState* thr = reinterpret_cast<ThreadState*>(*get_android_tls_ptr()); if (thr != dead_thread_state) { - __get_tls()[TLS_SLOT_TSAN] = dead_thread_state; + *get_android_tls_ptr() = reinterpret_cast<uptr>(dead_thread_state); UnmapOrDie(thr, sizeof(ThreadState)); } CHECK_EQ(0, internal_sigprocmask(SIG_SETMASK, &oldset, nullptr)); diff --git a/lib/tsan/rtl/tsan_report.cc b/lib/tsan/rtl/tsan_report.cc index af5fe61761d7..b188af2a0d48 100644 --- a/lib/tsan/rtl/tsan_report.cc +++ b/lib/tsan/rtl/tsan_report.cc @@ -164,7 +164,7 @@ static void PrintMop(const ReportMop *mop, bool first) { char thrbuf[kThreadBufSize]; Printf("%s", d.Access()); const char *object_type = GetObjectTypeFromTag(mop->external_tag); - if (!object_type) { + if (mop->external_tag == kExternalTagNone || !object_type) { Printf(" %s of size %d at %p by %s", MopDesc(first, mop->write, mop->atomic), mop->size, (void *)mop->addr, thread_name(thrbuf, mop->tid)); diff --git a/lib/tsan/rtl/tsan_rtl.cc b/lib/tsan/rtl/tsan_rtl.cc index 70393037e786..fa60f3247c38 100644 --- a/lib/tsan/rtl/tsan_rtl.cc +++ b/lib/tsan/rtl/tsan_rtl.cc @@ -866,9 +866,8 @@ static void MemoryRangeSet(ThreadState *thr, uptr pc, uptr addr, uptr size, // Don't want to touch lots of shadow memory. // If a program maps 10MB stack, there is no need reset the whole range. size = (size + (kShadowCell - 1)) & ~(kShadowCell - 1); - // UnmapOrDie/MmapFixedNoReserve does not work on Windows, - // so we do it only for C/C++. - if (SANITIZER_GO || size < common_flags()->clear_shadow_mmap_threshold) { + // UnmapOrDie/MmapFixedNoReserve does not work on Windows. + if (SANITIZER_WINDOWS || size < common_flags()->clear_shadow_mmap_threshold) { u64 *p = (u64*)MemToShadow(addr); CHECK(IsShadowMem((uptr)p)); CHECK(IsShadowMem((uptr)(p + size * kShadowCnt / kShadowCell - 1))); diff --git a/lib/tsan/rtl/tsan_rtl.h b/lib/tsan/rtl/tsan_rtl.h index 09c97a3a4f3d..8bf1c191ad76 100644 --- a/lib/tsan/rtl/tsan_rtl.h +++ b/lib/tsan/rtl/tsan_rtl.h @@ -411,7 +411,6 @@ struct ThreadState { bool is_dead; bool is_freeing; bool is_vptr_access; - uptr external_tag; const uptr stk_addr; const uptr stk_size; const uptr tls_addr; @@ -565,6 +564,16 @@ struct ScopedIgnoreInterceptors { } }; +enum ExternalTag : uptr { + kExternalTagNone = 0, + kExternalTagFirstUserAvailable = 1, + kExternalTagMax = 1024, + // Don't set kExternalTagMax over 65,536, since MBlock only stores tags + // as 16-bit values, see tsan_defs.h. +}; +const char *GetObjectTypeFromTag(uptr tag); +uptr TagFromShadowStackFrame(uptr pc); + class ScopedReport { public: explicit ScopedReport(ReportType typ); @@ -598,10 +607,26 @@ class ScopedReport { ThreadContext *IsThreadStackOrTls(uptr addr, bool *is_stack); void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, - MutexSet *mset); + MutexSet *mset, uptr *tag = nullptr); + +// The stack could look like: +// <start> | <main> | <foo> | tag | <bar> +// This will extract the tag and keep: +// <start> | <main> | <foo> | <bar> +template<typename StackTraceTy> +void ExtractTagFromStack(StackTraceTy *stack, uptr *tag = nullptr) { + if (stack->size < 2) return; + uptr possible_tag_pc = stack->trace[stack->size - 2]; + uptr possible_tag = TagFromShadowStackFrame(possible_tag_pc); + if (possible_tag == kExternalTagNone) return; + stack->trace_buffer[stack->size - 2] = stack->trace_buffer[stack->size - 1]; + stack->size -= 1; + if (tag) *tag = possible_tag; +} template<typename StackTraceTy> -void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) { +void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack, + uptr *tag = nullptr) { uptr size = thr->shadow_stack_pos - thr->shadow_stack; uptr start = 0; if (size + !!toppc > kStackTraceMax) { @@ -609,6 +634,7 @@ void ObtainCurrentStack(ThreadState *thr, uptr toppc, StackTraceTy *stack) { size = kStackTraceMax - !!toppc; } stack->Init(&thr->shadow_stack[start], size, toppc); + ExtractTagFromStack(stack, tag); } @@ -646,8 +672,6 @@ bool IsFiredSuppression(Context *ctx, ReportType type, StackTrace trace); bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); -const char *GetObjectTypeFromTag(uptr tag); - #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf #else @@ -739,7 +763,7 @@ void ProcUnwire(Processor *proc, ThreadState *thr); // Note: the parameter is called flagz, because flags is already taken // by the global function that returns flags. void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); -void MutexDestroy(ThreadState *thr, uptr pc, uptr addr); +void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0); void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz = 0, int rec = 1); diff --git a/lib/tsan/rtl/tsan_rtl_mutex.cc b/lib/tsan/rtl/tsan_rtl_mutex.cc index 086b28927919..54938f37e243 100644 --- a/lib/tsan/rtl/tsan_rtl_mutex.cc +++ b/lib/tsan/rtl/tsan_rtl_mutex.cc @@ -78,13 +78,13 @@ void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { s->mtx.Unlock(); } -void MutexDestroy(ThreadState *thr, uptr pc, uptr addr) { +void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) { DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr); StatInc(thr, StatMutexDestroy); SyncVar *s = ctx->metamap.GetIfExistsAndLock(addr, true); if (s == 0) return; - if (s->IsFlagSet(MutexFlagLinkerInit)) { + if ((flagz & MutexFlagLinkerInit) || s->IsFlagSet(MutexFlagLinkerInit)) { // Destroy is no-op for linker-initialized mutexes. s->mtx.Unlock(); return; diff --git a/lib/tsan/rtl/tsan_rtl_report.cc b/lib/tsan/rtl/tsan_rtl_report.cc index 5cd93a184ce7..055029b91f5b 100644 --- a/lib/tsan/rtl/tsan_rtl_report.cc +++ b/lib/tsan/rtl/tsan_rtl_report.cc @@ -377,7 +377,7 @@ const ReportDesc *ScopedReport::GetReport() const { } void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, - MutexSet *mset) { + MutexSet *mset, uptr *tag) { // This function restores stack trace and mutex set for the thread/epoch. // It does so by getting stack trace and mutex set at the beginning of // trace part, and then replaying the trace till the given epoch. @@ -436,6 +436,7 @@ void RestoreStack(int tid, const u64 epoch, VarSizeStackTrace *stk, return; pos++; stk->Init(&stack[0], pos); + ExtractTagFromStack(stk, tag); } static bool HandleRacyStacks(ThreadState *thr, VarSizeStackTrace traces[2], @@ -625,16 +626,15 @@ void ReportRace(ThreadState *thr) { typ = ReportTypeVptrRace; else if (freed) typ = ReportTypeUseAfterFree; - else if (thr->external_tag > 0) - typ = ReportTypeExternalRace; if (IsFiredSuppression(ctx, typ, addr)) return; const uptr kMop = 2; VarSizeStackTrace traces[kMop]; + uptr tags[kMop] = {kExternalTagNone}; const uptr toppc = TraceTopPC(thr); - ObtainCurrentStack(thr, toppc, &traces[0]); + ObtainCurrentStack(thr, toppc, &traces[0], &tags[0]); if (IsFiredSuppression(ctx, typ, traces[0])) return; @@ -644,18 +644,22 @@ void ReportRace(ThreadState *thr) { MutexSet *mset2 = new(&mset_buffer[0]) MutexSet(); Shadow s2(thr->racy_state[1]); - RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2); + RestoreStack(s2.tid(), s2.epoch(), &traces[1], mset2, &tags[1]); if (IsFiredSuppression(ctx, typ, traces[1])) return; if (HandleRacyStacks(thr, traces, addr_min, addr_max)) return; + // If any of the two accesses has a tag, treat this as an "external" race. + if (tags[0] != kExternalTagNone || tags[1] != kExternalTagNone) + typ = ReportTypeExternalRace; + ThreadRegistryLock l0(ctx->thread_registry); ScopedReport rep(typ); for (uptr i = 0; i < kMop; i++) { Shadow s(thr->racy_state[i]); - rep.AddMemoryAccess(addr, thr->external_tag, s, traces[i], + rep.AddMemoryAccess(addr, tags[i], s, traces[i], i == 0 ? &thr->mset : mset2); } diff --git a/lib/tsan/tests/rtl/tsan_posix.cc b/lib/tsan/tests/rtl/tsan_posix.cc index 9c0e013e5735..e66dab609c62 100644 --- a/lib/tsan/tests/rtl/tsan_posix.cc +++ b/lib/tsan/tests/rtl/tsan_posix.cc @@ -94,8 +94,9 @@ TEST(Posix, ThreadLocalAccesses) { // The test is failing with high thread count for aarch64. // FIXME: track down the issue and re-enable the test. // On Darwin, we're running unit tests without interceptors and __thread is -// using malloc and free, which causes false data race reports. -#if !defined(__aarch64__) && !defined(__APPLE__) +// using malloc and free, which causes false data race reports. On rare +// occasions on powerpc64le this test also fails. +#if !defined(__aarch64__) && !defined(__APPLE__) && !defined(powerpc64le) local_thread((void*)2); #endif } |
