[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