[compiler-rt] TSAN: Report when thread is not live and referenced in pthread (PR #156921)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 4 11:11:09 PDT 2025
https://github.com/ghostway0 updated https://github.com/llvm/llvm-project/pull/156921
>From f34691585ea2d0d59767f53de5547a5bee7b84ad Mon Sep 17 00:00:00 2001
From: ghostway <ghostway at tuta.io>
Date: Thu, 4 Sep 2025 19:26:31 +0300
Subject: [PATCH] TSAN: Report when thread is not live and referenced in
pthread
---
.../sanitizer_thread_registry.cpp | 13 ++++---
.../sanitizer_thread_registry.h | 2 +-
.../lib/tsan/rtl/tsan_interceptors_posix.cpp | 36 ++++++++++++++++---
compiler-rt/lib/tsan/rtl/tsan_rtl.h | 2 +-
compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp | 4 +--
compiler-rt/test/tsan/double_detach.c | 18 ++++++++++
compiler-rt/test/tsan/join_detach.c | 18 ++++++++++
7 files changed, 80 insertions(+), 13 deletions(-)
create mode 100644 compiler-rt/test/tsan/double_detach.c
create mode 100644 compiler-rt/test/tsan/join_detach.c
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
index d726d282437ca..076ab0ff10cc0 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
@@ -274,6 +274,7 @@ void ThreadRegistry::JoinThread(u32 tid, void *arg) {
{
ThreadRegistryLock l(this);
ThreadContextBase *tctx = threads_[tid];
+
CHECK_NE(tctx, 0);
if (tctx->status == ThreadStatusInvalid) {
Report("%s: Join of non-existent thread\n", SanitizerToolName);
@@ -357,17 +358,19 @@ ThreadContextBase *ThreadRegistry::QuarantinePop() {
return tctx;
}
-u32 ThreadRegistry::ConsumeThreadUserId(uptr user_id) {
+bool ThreadRegistry::ConsumeThreadUserId(uptr user_id, u32 *tid_out) {
ThreadRegistryLock l(this);
- u32 tid;
auto *t = live_.find(user_id);
- CHECK(t);
- tid = t->second;
+ if (!t) {
+ return false;
+ }
+ u32 tid = t->second;
live_.erase(t);
auto *tctx = threads_[tid];
CHECK_EQ(tctx->user_id, user_id);
tctx->user_id = 0;
- return tid;
+ *tid_out = tid;
+ return true;
}
void ThreadRegistry::SetThreadUserId(u32 tid, uptr user_id) {
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h
index 8adc420c8cce4..83459685fcb53 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h
@@ -135,7 +135,7 @@ class SANITIZER_MUTEX ThreadRegistry {
// Finishes thread and returns previous status.
ThreadStatus FinishThread(u32 tid);
void StartThread(u32 tid, ThreadID os_id, ThreadType thread_type, void *arg);
- u32 ConsumeThreadUserId(uptr user_id);
+ bool ConsumeThreadUserId(uptr user_id, u32 *tid_out);
void SetThreadUserId(u32 tid, uptr user_id);
// OnFork must be called in the child process after fork to purge old
diff --git a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
index b46a81031258c..aa5f4e2d1c5d9 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -1130,7 +1130,14 @@ TSAN_INTERCEPTOR(int, pthread_create,
TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
- Tid tid = ThreadConsumeTid(thr, pc, (uptr)th);
+ Tid tid;
+ if (!ThreadConsumeTid(thr, pc, (uptr)th, &tid)) {
+ Report(
+ "ThreadSanitizer: pthread_join was called on thread %d but it is "
+ "dead.\n",
+ thr->tid);
+ return -errno_EINVAL;
+ }
ThreadIgnoreBegin(thr, pc);
int res = BLOCK_REAL(pthread_join)(th, ret);
ThreadIgnoreEnd(thr);
@@ -1155,7 +1162,14 @@ int internal_pthread_join(void *th, void **ret) {
TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
SCOPED_INTERCEPTOR_RAW(pthread_detach, th);
- Tid tid = ThreadConsumeTid(thr, pc, (uptr)th);
+ Tid tid;
+ if (!ThreadConsumeTid(thr, pc, (uptr)th, &tid)) {
+ Report(
+ "ThreadSanitizer: pthread_detach was called on thread %d but it is "
+ "dead.\n",
+ thr->tid);
+ return -errno_EINVAL;
+ }
int res = REAL(pthread_detach)(th);
if (res == 0) {
ThreadDetach(thr, pc, tid);
@@ -1176,7 +1190,14 @@ TSAN_INTERCEPTOR(void, pthread_exit, void *retval) {
#if SANITIZER_LINUX
TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
SCOPED_INTERCEPTOR_RAW(pthread_tryjoin_np, th, ret);
- Tid tid = ThreadConsumeTid(thr, pc, (uptr)th);
+ Tid tid;
+ if (!ThreadConsumeTid(thr, pc, (uptr)th, &tid)) {
+ Report(
+ "ThreadSanitizer: pthread_tryjoin_np was called on thread %d but it is "
+ "dead.\n",
+ thr->tid);
+ return -errno_EINVAL;
+ }
ThreadIgnoreBegin(thr, pc);
int res = REAL(pthread_tryjoin_np)(th, ret);
ThreadIgnoreEnd(thr);
@@ -1190,7 +1211,14 @@ TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
const struct timespec *abstime) {
SCOPED_INTERCEPTOR_RAW(pthread_timedjoin_np, th, ret, abstime);
- Tid tid = ThreadConsumeTid(thr, pc, (uptr)th);
+ Tid tid;
+ if (!ThreadConsumeTid(thr, pc, (uptr)th, &tid)) {
+ Report(
+ "ThreadSanitizer: pthread_timedjoin_np was called on thread %d but it "
+ "is dead.\n",
+ thr->tid);
+ return -errno_EINVAL;
+ }
ThreadIgnoreBegin(thr, pc);
int res = BLOCK_REAL(pthread_timedjoin_np)(th, ret, abstime);
ThreadIgnoreEnd(thr);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index 0b6d5f088b142..03bd4d8de2e28 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl.h
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
@@ -563,7 +563,7 @@ Tid ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached);
void ThreadStart(ThreadState *thr, Tid tid, ThreadID os_id,
ThreadType thread_type);
void ThreadFinish(ThreadState *thr);
-Tid ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid);
+bool ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid, Tid *tid_out);
void ThreadJoin(ThreadState *thr, uptr pc, Tid tid);
void ThreadDetach(ThreadState *thr, uptr pc, Tid tid);
void ThreadFinalize(ThreadState *thr);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
index b1464ccfddeb4..fd33dde7e48db 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -301,8 +301,8 @@ struct ConsumeThreadContext {
ThreadContextBase *tctx;
};
-Tid ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) {
- return ctx->thread_registry.ConsumeThreadUserId(uid);
+bool ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid, Tid *tid_out) {
+ return ctx->thread_registry.ConsumeThreadUserId(uid, tid_out);
}
struct JoinArg {
diff --git a/compiler-rt/test/tsan/double_detach.c b/compiler-rt/test/tsan/double_detach.c
new file mode 100644
index 0000000000000..12fcf287f67b5
--- /dev/null
+++ b/compiler-rt/test/tsan/double_detach.c
@@ -0,0 +1,18 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <pthread.h>
+#include <stdio.h>
+
+void *fn(void *) { return NULL; }
+
+int main() {
+ pthread_t th;
+ int rc = pthread_create(&th, 0, fn, 0);
+ if (rc)
+ return rc;
+ pthread_detach(th);
+ pthread_detach(th);
+ return 0;
+}
+
+// CHECK: ThreadSanitizer: pthread_detach was called on thread 0 but it is dead.
diff --git a/compiler-rt/test/tsan/join_detach.c b/compiler-rt/test/tsan/join_detach.c
new file mode 100644
index 0000000000000..ee2e335ce25bb
--- /dev/null
+++ b/compiler-rt/test/tsan/join_detach.c
@@ -0,0 +1,18 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+
+#include <pthread.h>
+#include <stdio.h>
+
+void *fn(void *) { return NULL; }
+
+int main() {
+ pthread_t th;
+ int rc = pthread_create(&th, 0, fn, 0);
+ if (rc)
+ return rc;
+ pthread_join(th, NULL);
+ pthread_detach(th);
+ return 0;
+}
+
+// CHECK: ThreadSanitizer: pthread_detach was called on thread 0 but it is dead.
More information about the llvm-commits
mailing list