diff --git a/compiler-rt/include/sanitizer/tsan_interface.h b/compiler-rt/include/sanitizer/tsan_interface.h index 3ef79ab81dd53..e11a4175cd8ed 100644 --- a/compiler-rt/include/sanitizer/tsan_interface.h +++ b/compiler-rt/include/sanitizer/tsan_interface.h @@ -127,6 +127,10 @@ void SANITIZER_CDECL __tsan_mutex_post_signal(void *addr, unsigned flags); void SANITIZER_CDECL __tsan_mutex_pre_divert(void *addr, unsigned flags); void SANITIZER_CDECL __tsan_mutex_post_divert(void *addr, unsigned flags); +// Check that the current thread does not hold any mutexes, +// report a bug report otherwise. +void SANITIZER_CDECL __tsan_check_no_mutexes_held(); + // External race detection API. // Can be used by non-instrumented libraries to detect when their objects are // being used in an unsafe manner. diff --git a/compiler-rt/lib/tsan/rtl/tsan.syms.extra b/compiler-rt/lib/tsan/rtl/tsan.syms.extra index a5bd17176b12b..6416e5d47fc41 100644 --- a/compiler-rt/lib/tsan/rtl/tsan.syms.extra +++ b/compiler-rt/lib/tsan/rtl/tsan.syms.extra @@ -22,6 +22,7 @@ __tsan_mutex_pre_signal __tsan_mutex_post_signal __tsan_mutex_pre_divert __tsan_mutex_post_divert +__tsan_check_no_mutexes_held __tsan_get_current_fiber __tsan_create_fiber __tsan_destroy_fiber diff --git a/compiler-rt/lib/tsan/rtl/tsan_debugging.cpp b/compiler-rt/lib/tsan/rtl/tsan_debugging.cpp index 1e61c31c5a970..41fa293dbaaad 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_debugging.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_debugging.cpp @@ -35,7 +35,9 @@ static const char *ReportTypeDescription(ReportType typ) { case ReportTypeSignalUnsafe: return "signal-unsafe-call"; case ReportTypeErrnoInSignal: return "errno-in-signal-handler"; case ReportTypeDeadlock: return "lock-order-inversion"; - // No default case so compiler warns us if we miss one + case ReportTypeMutexHeldWrongContext: + return "mutex-held-in-wrong-context"; + // No default case so compiler warns us if we miss one } UNREACHABLE("missing case"); } diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp index 6bd72e18d9425..5154662034c56 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp @@ -435,4 +435,26 @@ void __tsan_mutex_post_divert(void *addr, unsigned flagz) { ThreadIgnoreBegin(thr, 0); ThreadIgnoreSyncBegin(thr, 0); } + +static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) { + ThreadRegistryLock l(&ctx->thread_registry); + ScopedReport rep(ReportTypeMutexHeldWrongContext); + for (uptr i = 0; i < thr->mset.Size(); ++i) { + MutexSet::Desc desc = thr->mset.Get(i); + rep.AddMutex(desc.addr, desc.stack_id); + } + VarSizeStackTrace trace; + ObtainCurrentStack(thr, pc, &trace); + rep.AddStack(trace, true); + OutputReport(thr, rep); +} + +INTERFACE_ATTRIBUTE +void __tsan_check_no_mutexes_held() { + SCOPED_ANNOTATION(__tsan_check_no_mutexes_held); + if (thr->mset.Size() == 0) { + return; + } + ReportMutexHeldWrongContext(thr, pc); +} } // extern "C" diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_report.cpp index 4028d18107840..35cb6710a54fa 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_report.cpp @@ -93,7 +93,9 @@ static const char *ReportTypeString(ReportType typ, uptr tag) { return "signal handler spoils errno"; case ReportTypeDeadlock: return "lock-order-inversion (potential deadlock)"; - // No default case so compiler warns us if we miss one + case ReportTypeMutexHeldWrongContext: + return "mutex held in the wrong context"; + // No default case so compiler warns us if we miss one } UNREACHABLE("missing case"); } diff --git a/compiler-rt/lib/tsan/rtl/tsan_report.h b/compiler-rt/lib/tsan/rtl/tsan_report.h index 3c88864af1477..bfe470797f8f7 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_report.h +++ b/compiler-rt/lib/tsan/rtl/tsan_report.h @@ -34,7 +34,8 @@ enum ReportType { ReportTypeMutexBadReadUnlock, ReportTypeSignalUnsafe, ReportTypeErrnoInSignal, - ReportTypeDeadlock + ReportTypeDeadlock, + ReportTypeMutexHeldWrongContext }; struct ReportStack { diff --git a/compiler-rt/lib/tsan/rtl/tsan_suppressions.cpp b/compiler-rt/lib/tsan/rtl/tsan_suppressions.cpp index 9cdfa32a93430..70642124990d7 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_suppressions.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_suppressions.cpp @@ -81,6 +81,7 @@ static const char *conv(ReportType typ) { case ReportTypeMutexBadUnlock: case ReportTypeMutexBadReadLock: case ReportTypeMutexBadReadUnlock: + case ReportTypeMutexHeldWrongContext: return kSuppressionMutex; case ReportTypeSignalUnsafe: case ReportTypeErrnoInSignal: diff --git a/compiler-rt/test/tsan/mutex_held_wrong_context.cpp b/compiler-rt/test/tsan/mutex_held_wrong_context.cpp new file mode 100644 index 0000000000000..68a6761891756 --- /dev/null +++ b/compiler-rt/test/tsan/mutex_held_wrong_context.cpp @@ -0,0 +1,34 @@ +// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s +#include "test.h" + +pthread_mutex_t mtx; + +__attribute__((noinline)) void Func1() { + pthread_mutex_lock(&mtx); + __tsan_check_no_mutexes_held(); + pthread_mutex_unlock(&mtx); +} + +__attribute__((noinline)) void Func2() { + pthread_mutex_lock(&mtx); + pthread_mutex_unlock(&mtx); + __tsan_check_no_mutexes_held(); +} + +int main() { + pthread_mutex_init(&mtx, NULL); + Func1(); + Func2(); + return 0; +} + +// CHECK: WARNING: ThreadSanitizer: mutex held in the wrong context +// CHECK: {{.*}}__tsan_check_no_mutexes_held{{.*}} +// CHECK: {{.*}}Func1{{.*}} +// CHECK: {{.*}}main{{.*}} +// CHECK: Mutex {{.*}} created at: +// CHECK: {{.*}}pthread_mutex_init{{.*}} +// CHECK: {{.*}}main{{.*}} +// CHECK: SUMMARY: ThreadSanitizer: mutex held in the wrong context {{.*}}mutex_held_wrong_context.cpp{{.*}}Func1 + +// CHECK-NOT: SUMMARY: ThreadSanitizer: mutex held in the wrong context {{.*}}mutex_held_wrong_context.cpp{{.*}}Func2