Index: lib/tsan/rtl/tsan_debugging.cc =================================================================== --- lib/tsan/rtl/tsan_debugging.cc +++ lib/tsan/rtl/tsan_debugging.cc @@ -25,6 +25,7 @@ if (typ == ReportTypeUseAfterFree) return "heap-use-after-free"; if (typ == ReportTypeVptrUseAfterFree) return "heap-use-after-free-vptr"; if (typ == ReportTypeExternalRace) return "external-race"; + if (typ == ReportTypeSwiftAccessRace) return "swift-access-race"; if (typ == ReportTypeThreadLeak) return "thread-leak"; if (typ == ReportTypeMutexDestroyLocked) return "locked-mutex-destroy"; if (typ == ReportTypeMutexDoubleLock) return "mutex-double-lock"; Index: lib/tsan/rtl/tsan_external.cc =================================================================== --- lib/tsan/rtl/tsan_external.cc +++ lib/tsan/rtl/tsan_external.cc @@ -16,11 +16,8 @@ #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; @@ -29,11 +26,34 @@ 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]; +} + +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(); + if (caller_pc) FuncEntry(thr, (uptr)caller_pc); + InsertShadowStackFrameForTag(thr, (uptr)tag); + access(thr, CALLERPC, (uptr)addr, kSizeLog8); + FuncExit(thr); + if (caller_pc) FuncExit(thr); +} + 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; } @@ -54,24 +74,12 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_read(void *addr, void *caller_pc, void *tag) { - CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); - ThreadState *thr = cur_thread(); - thr->external_tag = (uptr)tag; - FuncEntry(thr, (uptr)caller_pc); - MemoryRead(thr, CALLERPC, (uptr)addr, kSizeLog8); - FuncExit(thr); - thr->external_tag = 0; + ExternalAccess(addr, caller_pc, tag, MemoryRead); } SANITIZER_INTERFACE_ATTRIBUTE void __tsan_external_write(void *addr, void *caller_pc, void *tag) { - CHECK_LT(tag, atomic_load(&used_tags, memory_order_relaxed)); - ThreadState *thr = cur_thread(); - thr->external_tag = (uptr)tag; - FuncEntry(thr, (uptr)caller_pc); - MemoryWrite(thr, CALLERPC, (uptr)addr, kSizeLog8); - FuncExit(thr); - thr->external_tag = 0; + ExternalAccess(addr, caller_pc, tag, MemoryWrite); } } // extern "C" Index: lib/tsan/rtl/tsan_report.h =================================================================== --- lib/tsan/rtl/tsan_report.h +++ lib/tsan/rtl/tsan_report.h @@ -25,6 +25,7 @@ ReportTypeUseAfterFree, ReportTypeVptrUseAfterFree, ReportTypeExternalRace, + ReportTypeSwiftAccessRace, ReportTypeThreadLeak, ReportTypeMutexDestroyLocked, ReportTypeMutexDoubleLock, Index: lib/tsan/rtl/tsan_report.cc =================================================================== --- lib/tsan/rtl/tsan_report.cc +++ lib/tsan/rtl/tsan_report.cc @@ -92,6 +92,8 @@ return "heap-use-after-free (virtual call vs free)"; if (typ == ReportTypeExternalRace) return "race on a library object"; + if (typ == ReportTypeSwiftAccessRace) + return "Swift access race"; if (typ == ReportTypeThreadLeak) return "thread leak"; if (typ == ReportTypeMutexDestroyLocked) @@ -159,12 +161,20 @@ : (write ? "Previous mutating" : "Previous read-only"); } +static const char *SwiftMopDesc(bool first) { + return first ? "Modifying" : "Previous modifying"; +} + static void PrintMop(const ReportMop *mop, bool first) { Decorator d; char thrbuf[kThreadBufSize]; Printf("%s", d.Access()); const char *object_type = GetObjectTypeFromTag(mop->external_tag); - if (!object_type) { + if (mop->external_tag == kExternalTagSwiftModifyingAccess) { + Printf(" %s access at %p by %s", + SwiftMopDesc(first), (void *)mop->addr, + thread_name(thrbuf, mop->tid)); + } else 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)); Index: lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h @@ -410,7 +410,6 @@ 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; @@ -580,6 +579,7 @@ void AddLocation(uptr addr, uptr size); void AddSleep(u32 stack_id); void SetCount(int count); + void SetType(ReportType typ); const ReportDesc *GetReport() const; @@ -645,7 +645,16 @@ bool IsExpectedReport(uptr addr, uptr size); void PrintMatchedBenignRaces(); +enum ExternalTag : uptr { + kExternalTagNone = 0, + kExternalTagSwiftModifyingAccess = 1, + kExternalTagFirstUserAvailable = 128, + 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); #if defined(TSAN_DEBUG_OUTPUT) && TSAN_DEBUG_OUTPUT >= 1 # define DPrintf Printf Index: lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_report.cc +++ lib/tsan/rtl/tsan_rtl_report.cc @@ -372,6 +372,10 @@ rep_->count = count; } +void ScopedReport::SetType(ReportType typ) { + rep_->typ = typ; +} + const ReportDesc *ScopedReport::GetReport() const { return rep_; } @@ -585,6 +589,15 @@ return false; } +static uptr RetrieveTagFromStackTrace(VarSizeStackTrace *trace) { + uptr possible_tag_pc = trace->trace[trace->size - 2]; + uptr tag = TagFromShadowStackFrame(possible_tag_pc); + if (tag == kExternalTagNone) return kExternalTagNone; + trace->trace_buffer[trace->size - 2] = trace->trace_buffer[trace->size - 1]; + trace->size -= 1; + return tag; +} + void ReportRace(ThreadState *thr) { CheckNoLocks(thr); @@ -625,8 +638,6 @@ typ = ReportTypeVptrRace; else if (freed) typ = ReportTypeUseAfterFree; - else if (thr->external_tag > 0) - typ = ReportTypeExternalRace; if (IsFiredSuppression(ctx, typ, addr)) return; @@ -654,9 +665,15 @@ ThreadRegistryLock l0(ctx->thread_registry); ScopedReport rep(typ); for (uptr i = 0; i < kMop; i++) { + uptr tag = RetrieveTagFromStackTrace(&traces[i]); + if (tag == kExternalTagSwiftModifyingAccess) { + rep.SetType(ReportTypeSwiftAccessRace); + } else if (tag != kExternalTagNone) { + rep.SetType(ReportTypeExternalRace); + } + Shadow s(thr->racy_state[i]); - rep.AddMemoryAccess(addr, thr->external_tag, s, traces[i], - i == 0 ? &thr->mset : mset2); + rep.AddMemoryAccess(addr, tag, s, traces[i], i == 0 ? &thr->mset : mset2); } for (uptr i = 0; i < kMop; i++) { Index: lib/tsan/rtl/tsan_suppressions.cc =================================================================== --- lib/tsan/rtl/tsan_suppressions.cc +++ lib/tsan/rtl/tsan_suppressions.cc @@ -76,6 +76,8 @@ return kSuppressionRace; else if (typ == ReportTypeExternalRace) return kSuppressionRace; + else if (typ == ReportTypeSwiftAccessRace) + return kSuppressionRace; else if (typ == ReportTypeThreadLeak) return kSuppressionThread; else if (typ == ReportTypeMutexDestroyLocked) Index: test/tsan/Darwin/external-swift.cc =================================================================== --- test/tsan/Darwin/external-swift.cc +++ test/tsan/Darwin/external-swift.cc @@ -0,0 +1,92 @@ +// RUN: %clangxx_tsan %s -o %t +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#include + +#import "../test.h" + +static void *tag = (void *)0x1; +extern "C" { +void *__tsan_external_register_tag(const char *object_type); +void *__tsan_external_assign_tag(void *addr, void *tag); +void __tsan_external_read(void *addr, void *caller_pc, void *tag); +void __tsan_external_write(void *addr, void *caller_pc, void *tag); +void __tsan_write8(void *addr); +} + +__attribute__((no_sanitize("thread"))) +void ExternalWrite(void *addr) { + __tsan_external_write(addr, nullptr, tag); +} + +__attribute__((no_sanitize("thread"))) +void RegularWrite(void *addr) { + __tsan_write8(addr); +} + +int main(int argc, char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "Start.\n"); + // CHECK: Start. + + { + void *opaque_object = malloc(16); + std::thread t1([opaque_object] { + ExternalWrite(opaque_object); + barrier_wait(&barrier); + }); + std::thread t2([opaque_object] { + barrier_wait(&barrier); + ExternalWrite(opaque_object); + }); + // CHECK: WARNING: ThreadSanitizer: Swift access race + // CHECK: Modifying access at {{.*}} by thread {{.*}} + // CHECK: Previous modifying access at {{.*}} by thread {{.*}} + t1.join(); + t2.join(); + } + + fprintf(stderr, "external+external test done.\n"); + // CHECK: external+external test done. + + { + void *opaque_object = malloc(16); + std::thread t1([opaque_object] { + ExternalWrite(opaque_object); + barrier_wait(&barrier); + }); + std::thread t2([opaque_object] { + barrier_wait(&barrier); + RegularWrite(opaque_object); + }); + // CHECK: WARNING: ThreadSanitizer: Swift access race + // CHECK: Write of size 8 at {{.*}} by thread {{.*}} + // CHECK: Previous modifying access at {{.*}} by thread {{.*}} + t1.join(); + t2.join(); + } + + fprintf(stderr, "external+regular test done.\n"); + // CHECK: external+regular test done. + + { + void *opaque_object = malloc(16); + std::thread t1([opaque_object] { + RegularWrite(opaque_object); + barrier_wait(&barrier); + }); + std::thread t2([opaque_object] { + barrier_wait(&barrier); + ExternalWrite(opaque_object); + }); + // CHECK: WARNING: ThreadSanitizer: Swift access race + // CHECK: Modifying access at {{.*}} by thread {{.*}} + // CHECK: Previous write of size 8 at {{.*}} by thread {{.*}} + t1.join(); + t2.join(); + } + + fprintf(stderr, "regular+external test done.\n"); + // CHECK: regular+external test done. +} +