[llvm-branch-commits] [compiler-rt] [NFC][TSan] Move ObtainCurrentStack out of ThreadRegistryLock scope (PR #228794)

Vitaly Buka via llvm-branch-commits llvm-branch-commits at lists.llvm.org
Sat Oct 3 23:38:27 PDT 2026


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 <vitalybuka at google.com>
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 <vitalybuka at google.com>
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);



More information about the llvm-branch-commits mailing list