[compiler-rt] [TSan] Release locks before OutputReport on all platforms (PR #228554)

Vitaly Buka via llvm-commits llvm-commits at lists.llvm.org
Fri Oct 2 13:54:22 PDT 2026


https://github.com/vitalybuka updated https://github.com/llvm/llvm-project/pull/228554

>From 2355b3adb96e873af15435712e4ff9239230571e Mon Sep 17 00:00:00 2001
From: Vitaly Buka <vitalybuka at google.com>
Date: Fri, 2 Oct 2026 12:11:03 -0700
Subject: [PATCH] =?UTF-8?q?[=F0=9D=98=80=F0=9D=97=BD=F0=9D=97=BF]=20initia?=
 =?UTF-8?q?l=20version?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Created using spr 1.3.7
---
 .../lib/tsan/rtl/tsan_interceptors_posix.cpp  | 19 +++----
 .../lib/tsan/rtl/tsan_interface_ann.cpp       | 17 +++----
 compiler-rt/lib/tsan/rtl/tsan_mman.cpp        | 17 +++----
 compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp   | 51 +++++++------------
 compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp  | 18 +++----
 compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp  | 17 +++----
 .../test/tsan/signal_abort_on_error.cpp       | 41 +++++++++++++++
 7 files changed, 90 insertions(+), 90 deletions(-)
 create mode 100644 compiler-rt/test/tsan/signal_abort_on_error.cpp

diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index c0a75a53e5a1faa..b2ba666741e8918 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -2179,8 +2179,8 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) {
   // Use alloca, because malloc during signal handling deadlocks
   ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
   bool suppressed;
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     ThreadRegistryLock l(&ctx->thread_registry);
     new (rep) ScopedReport(ReportTypeErrnoInSignal);
@@ -2188,17 +2188,12 @@ static void ReportErrnoSpoiling(ThreadState *thr, uptr pc, int sig) {
     suppressed = IsFiredSuppression(ctx, ReportTypeErrnoInSignal, stack);
     if (!suppressed)
       rep->AddStack(stack, true);
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks before writing report
-#endif
-    if (!suppressed)
-      OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  if (!suppressed)
+    OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 static void CallUserSignalHandler(ThreadState *thr, bool sync, bool acquire,
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
index 490f6cb3bce01b4..526dc5a753c24f3 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interface_ann.cpp
@@ -443,8 +443,8 @@ void __tsan_mutex_post_divert(void *addr, unsigned flagz) {
 static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) {
   // Use alloca, because malloc during signal handling deadlocks
   ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     ThreadRegistryLock l(&ctx->thread_registry);
     new (rep) ScopedReport(ReportTypeMutexHeldWrongContext);
@@ -455,16 +455,11 @@ static void ReportMutexHeldWrongContext(ThreadState *thr, uptr pc) {
     VarSizeStackTrace trace;
     ObtainCurrentStack(thr, pc, &trace);
     rep->AddStack(trace, true);
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks
-#endif
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 INTERFACE_ATTRIBUTE
diff --git a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
index caacb3675825f92..cea833054551d34 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_mman.cpp
@@ -184,22 +184,17 @@ static void SignalUnsafeCall(ThreadState *thr, uptr pc) {
     return;
   // Use alloca, because malloc during signal handling deadlocks
   ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     ThreadRegistryLock l(&ctx->thread_registry);
     new (rep) ScopedReport(ReportTypeSignalUnsafe);
     rep->AddStack(stack, true);
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks
-#endif
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
index 30f5e964939d034..bc2c62338c26281 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_mutex.cpp
@@ -58,8 +58,8 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
     return;
   // Use alloca, because malloc during signal handling deadlocks
   ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     ThreadRegistryLock l(&ctx->thread_registry);
     new (rep) ScopedReport(typ);
@@ -68,16 +68,11 @@ static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
     ObtainCurrentStack(thr, pc, &trace);
     rep->AddStack(trace, true);
     rep->AddLocation(addr, 1);
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks
-#endif
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 static void RecordMutexLock(ThreadState *thr, uptr pc, uptr addr,
@@ -545,8 +540,8 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
     return;
   // Use alloca, because malloc during signal handling deadlocks
   ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     ThreadRegistryLock l(&ctx->thread_registry);
     new (rep) ScopedReport(ReportTypeDeadlock);
@@ -570,24 +565,19 @@ void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
         rep->AddStack(stack, true);
       }
     }
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks
-#endif
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 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));
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     // We need to lock the slot during RestoreStack because it protects
     // the slot journal.
@@ -608,16 +598,11 @@ void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
       return;
     rep->AddStack(trace, true);
     rep->AddLocation(addr, 1);
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks
-#endif
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 }  // namespace __tsan
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
index 4e58305b582d5a0..0cace30fec10a53 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_report.cpp
@@ -815,8 +815,8 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
 
   // Use alloca, because malloc during signal handling deadlocks
   ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
-  // Take a new scope as Apple platforms require the below locks released
-  // before symbolizing in order to avoid a deadlock
+  // Release locks before symbolizing and outputting the report to avoid
+  // deadlocks.
   {
     // We need to lock the slot during RestoreStack because it protects
     // the slot journal.
@@ -876,17 +876,11 @@ void ReportRace(ThreadState *thr, RawShadow *shadow_mem, Shadow cur, Shadow old,
         s[1].epoch() <= thr->last_sleep_clock.Get(s[1].sid()))
       rep->AddSleep(thr->last_sleep_stack_id);
 #endif
-
-#if SANITIZER_APPLE
-  }  // Close this scope to release the locks
-#endif
-    OutputReport(thr, *rep);
-
-    // Need to manually destroy this because we used placement new to allocate
-    rep->~ScopedReport();
-#if !SANITIZER_APPLE
   }
-#endif
+  OutputReport(thr, *rep);
+
+  // Need to manually destroy this because we used placement new to allocate
+  rep->~ScopedReport();
 }
 
 void PrintCurrentStack(ThreadState *thr, uptr pc) {
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index 978d853b0bc7e18..afc6b8d4a0b1cdc 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -98,23 +98,18 @@ void ThreadFinalize(ThreadState *thr) {
   for (uptr i = 0; i < leaks.Size(); i++) {
     // Use alloca, because malloc during signal handling deadlocks
     ScopedReport *rep = (ScopedReport *)__builtin_alloca(sizeof(ScopedReport));
-    // Take a new scope as Apple platforms require the below locks released
-    // before symbolizing in order to avoid a deadlock
+    // Release locks before symbolizing and outputting the report to avoid
+    // deadlocks.
     {
       ThreadRegistryLock l(&ctx->thread_registry);
       new (rep) ScopedReport(ReportTypeThreadLeak);
       rep->AddThread(leaks[i].tctx, true);
       rep->SetCount(leaks[i].count);
-#  if SANITIZER_APPLE
-    }  // Close this scope to release the locks
-#  endif
-      OutputReport(thr, *rep);
-
-      // Need to manually destroy this because we used placement new to allocate
-      rep->~ScopedReport();
-#  if !SANITIZER_APPLE
     }
-#  endif
+    OutputReport(thr, *rep);
+
+    // Need to manually destroy this because we used placement new to allocate
+    rep->~ScopedReport();
   }
 #endif
 }
diff --git a/compiler-rt/test/tsan/signal_abort_on_error.cpp b/compiler-rt/test/tsan/signal_abort_on_error.cpp
new file mode 100644
index 000000000000000..4f119badf9efa4e
--- /dev/null
+++ b/compiler-rt/test/tsan/signal_abort_on_error.cpp
@@ -0,0 +1,41 @@
+// RUN: %clang_tsan -O1 %s -o %t && %env_tsan_opts=halt_on_error=1:abort_on_error=1:handle_abort=0 %deflake %run %t 2>&1 | FileCheck %s
+#include "test.h"
+#include <signal.h>
+
+int Global;
+
+__attribute__((noinline)) static void step(int *p, int i) { *p = i; }
+
+static void handler(int, siginfo_t *, void *) {
+  // Overflow the active TracePart in the SIGABRT handler to trigger
+  // TracePartAlloc, which acquires ctx->slot_mtx.
+  int x = 0;
+  for (int i = 0; i < 10000; ++i)
+    step(&x, i);
+  write(2, "SIGNAL\n", 7);
+  _exit(0);
+}
+
+void *Thread(void *x) {
+  Global = 42;
+  barrier_wait(&barrier);
+  return NULL;
+}
+
+int main() {
+  struct sigaction act = {};
+  act.sa_sigaction = &handler;
+  act.sa_flags = SA_SIGINFO;
+  sigaction(SIGABRT, &act, 0);
+
+  barrier_init(&barrier, 2);
+  pthread_t t;
+  pthread_create(&t, NULL, Thread, NULL);
+  pthread_detach(t);
+  barrier_wait(&barrier);
+  Global = 43;
+  return 0;
+}
+
+// CHECK: WARNING: ThreadSanitizer: data race
+// CHECK: SIGNAL



More information about the llvm-commits mailing list