mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-23 22:00:10 +00:00
[TSAN] Add __tsan_check_no_mutexes_held helper (#69372)
This adds a new helper that can be called from application code to ensure that no mutexes are held on specific code paths. This is useful for multiple scenarios, including ensuring no locks are held: - at thread exit - in peformance-critical code - when a coroutine is suspended (can cause deadlocks) See this discourse thread for more discussion: https://discourse.llvm.org/t/add-threadsanitizer-check-to-prevent-coroutine-suspending-while-holding-a-lock-potential-deadlock/74051
This commit is contained in:
parent
3cef582ae4
commit
bd841111f3
@ -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 __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.
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
||||
|
@ -34,7 +34,8 @@ enum ReportType {
|
||||
ReportTypeMutexBadReadUnlock,
|
||||
ReportTypeSignalUnsafe,
|
||||
ReportTypeErrnoInSignal,
|
||||
ReportTypeDeadlock
|
||||
ReportTypeDeadlock,
|
||||
ReportTypeMutexHeldWrongContext
|
||||
};
|
||||
|
||||
struct ReportStack {
|
||||
|
@ -81,6 +81,7 @@ static const char *conv(ReportType typ) {
|
||||
case ReportTypeMutexBadUnlock:
|
||||
case ReportTypeMutexBadReadLock:
|
||||
case ReportTypeMutexBadReadUnlock:
|
||||
case ReportTypeMutexHeldWrongContext:
|
||||
return kSuppressionMutex;
|
||||
case ReportTypeSignalUnsafe:
|
||||
case ReportTypeErrnoInSignal:
|
||||
|
34
compiler-rt/test/tsan/mutex_held_wrong_context.cpp
Normal file
34
compiler-rt/test/tsan/mutex_held_wrong_context.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
|
||||
#include "test.h"
|
||||
|
||||
pthread_mutex_t mtx;
|
||||
|
||||
void Func1() {
|
||||
pthread_mutex_lock(&mtx);
|
||||
__tsan_check_no_mutexes_held();
|
||||
pthread_mutex_unlock(&mtx);
|
||||
}
|
||||
|
||||
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: #0 __tsan_check_no_mutexes_held
|
||||
// CHECK: #1 Func1
|
||||
// CHECK: #2 main
|
||||
// CHECK: Mutex {{.*}} created at:
|
||||
// CHECK: #0 pthread_mutex_init
|
||||
// CHECK: #1 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
|
Loading…
Reference in New Issue
Block a user