https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/228794
>From b4525cafa3f5299915552fbc6f3ec07e80c78817 Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Sat, 3 Oct 2026 19:00:52 -0700 Subject: [PATCH 1/2] [NFC][TSan] Move RestoreStack before ScopedReport in ReportDestroyLocked Run RestoreStack in its own lock scope before constructing ScopedReport in ReportDestroyLocked (matching ReportRace). This avoids acquiring ScopedErrorReportLock or symbolizing the current stack if RestoreStack fails, and avoids holding slot_lock and slot_mtx while populating the ScopedReport. Assisted-by: Gemini Pull Request: https://github.com/llvm/llvm-project/pull/228643 --- compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp | 29 ++++++++++++--------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp index da3604a2b840d29..271412f4c408853 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp @@ -574,31 +574,34 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr, FastState last_lock, StackID creation_stack_id) { - // Use alloca, because malloc during signal handling deadlocks - ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport)); - // Release locks before symbolizing and outputting the report to avoid - // deadlocks. + VarSizeStackTrace last_lock_stack; { // We need to lock the slot during RestoreStack because it protects // the slot journal. Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx); ThreadRegistryLock l0(&ctx->thread_registry); Lock slots_lock(&ctx->slot_mtx); - new (rep) ScopedReport(ReportTypeMutexDestroyLocked); - rep->AddMutex(addr, creation_stack_id); - VarSizeStackTrace trace; - ObtainCurrentStack(thr, pc, &trace); - rep->AddStack(trace, true); - Tid tid; DynamicMutexSet mset; uptr tag; if (!RestoreStack(EventType::kLock, last_lock.sid(), last_lock.epoch(), - addr, 0, kAccessWrite, &tid, &trace, mset, &tag)) { - rep->~ScopedReport(); + addr, 0, kAccessWrite, &tid, &last_lock_stack, mset, + &tag)) return; - } + } + + // Use alloca, because malloc during signal handling deadlocks + ScopedReport* rep = (ScopedReport*)__builtin_alloca(sizeof(ScopedReport)); + // Release locks before symbolizing and outputting the report to avoid + // deadlocks. + { + ThreadRegistryLock l0(&ctx->thread_registry); + new (rep) ScopedReport(ReportTypeMutexDestroyLocked); + rep->AddMutex(addr, creation_stack_id); + VarSizeStackTrace trace; + ObtainCurrentStack(thr, pc, &trace); rep->AddStack(trace, true); + rep->AddStack(last_lock_stack, true); rep->AddLocation(addr, 1); } OutputReport(thr, *rep); >From 5729303d68d2a4ad588e6e4126ac477073e09b96 Mon Sep 17 00:00:00 2001 From: Vitaly Buka <[email protected]> Date: Sat, 3 Oct 2026 19:00:54 -0700 Subject: [PATCH 2/2] [NFC][TSan] Move ObtainCurrentStack out of ThreadRegistryLock scope ObtainCurrentStack only reads the current thread's shadow stack and allocates a VarSizeStackTrace buffer, which does not require ThreadRegistryLock (and already runs outside ThreadRegistryLock in ReportRace, SignalUnsafeCall, and ReportErrnoSpoiling). Move ObtainCurrentStack (and dummy_pc in ReportDeadlock) before ScopedReport in ReportMutexHeldWrongContext, ReportMutexMisuse, ReportDeadlock, and ReportDestroyLocked so the stack trace buffers also outlive ScopedReport and OutputReport. Assisted-by: Gemini Pull Request: https://github.com/llvm/llvm-project/pull/228794 --- compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp | 4 ++-- compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp index 526dc5a753c24f3..e520ead1d36aa4a 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp @@ -441,6 +441,8 @@ void __tsan_mutex_post_divert(void *addr, unsigned flagz) { } static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) { + VarSizeStackTrace trace; + ObtainCurrentStack(thr, pc, &trace); // Use alloca, because malloc during signal handling deadlocks ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport)); // Release locks before symbolizing and outputting the report to avoid @@ -452,8 +454,6 @@ static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) { 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); diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp index 271412f4c408853..71f77596c8ae221 100644 --- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp +++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp @@ -56,6 +56,8 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ, return; if (!ShouldReport(thr, typ)) return; + VarSizeStackTrace trace; + ObtainCurrentStack(thr, pc, &trace); // Use alloca, because malloc during signal handling deadlocks ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport)); // Release locks before symbolizing and outputting the report to avoid @@ -64,8 +66,6 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ, ThreadRegistryLock l(&ctx->thread_registry); new (rep) ScopedReport(typ); rep->AddMutex(addr, creation_stack_id); - VarSizeStackTrace trace; - ObtainCurrentStack(thr, pc, &trace); rep->AddStack(trace, true); rep->AddLocation(addr, 1); } @@ -538,6 +538,7 @@ void AfterSleep(ThreadState *thr, uptr pc) { void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { if (r == 0 || !ShouldReport(thr, ReportTypeDeadlock)) return; + uptr dummy_pc = 0x42; // Use alloca, because malloc during signal handling deadlocks ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport)); // Release locks before symbolizing and outputting the report to avoid @@ -550,7 +551,6 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { rep->AddUniqueTid((int)r->loop[i].thr_ctx); rep->AddThread((int)r->loop[i].thr_ctx); } - uptr dummy_pc = 0x42; for (int i = 0; i < r->n; i++) { for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) { u32 stk = r->loop[i].stk[j]; @@ -590,6 +590,9 @@ void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr, return; } + VarSizeStackTrace trace; + ObtainCurrentStack(thr, pc, &trace); + // Use alloca, because malloc during signal handling deadlocks ScopedReport* rep = (ScopedReport*)__builtin_alloca(sizeof(ScopedReport)); // Release locks before symbolizing and outputting the report to avoid @@ -598,8 +601,6 @@ void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr, ThreadRegistryLock l0(&ctx->thread_registry); new (rep) ScopedReport(ReportTypeMutexDestroyLocked); rep->AddMutex(addr, creation_stack_id); - VarSizeStackTrace trace; - ObtainCurrentStack(thr, pc, &trace); rep->AddStack(trace, true); rep->AddStack(last_lock_stack, true); rep->AddLocation(addr, 1); _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
