Skip to content

[TSAN] Add __tsan_check_no_mutexes_held helper #69372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions compiler-rt/include/sanitizer/tsan_interface.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,10 @@ void __tsan_mutex_post_signal(void *addr, unsigned flags);
void __tsan_mutex_pre_divert(void *addr, unsigned flags);
void __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.
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/tsan/rtl/tsan.syms.extra
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion compiler-rt/lib/tsan/rtl/tsan_debugging.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
22 changes: 22 additions & 0 deletions compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
4 changes: 3 additions & 1 deletion compiler-rt/lib/tsan/rtl/tsan_report.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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");
}
Expand Down
3 changes: 2 additions & 1 deletion compiler-rt/lib/tsan/rtl/tsan_report.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ enum ReportType {
ReportTypeMutexBadReadUnlock,
ReportTypeSignalUnsafe,
ReportTypeErrnoInSignal,
ReportTypeDeadlock
ReportTypeDeadlock,
ReportTypeMutexHeldWrongContext
};

struct ReportStack {
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/tsan/rtl/tsan_suppressions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ static const char *conv(ReportType typ) {
case ReportTypeMutexBadUnlock:
case ReportTypeMutexBadReadLock:
case ReportTypeMutexBadReadUnlock:
case ReportTypeMutexHeldWrongContext:
return kSuppressionMutex;
case ReportTypeSignalUnsafe:
case ReportTypeErrnoInSignal:
Expand Down
34 changes: 34 additions & 0 deletions compiler-rt/test/tsan/mutex_held_wrong_context.cpp
Original file line number Diff line number Diff line change
@@ -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