[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 09:33:56 PDT 2025


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-compiler-rt-sanitizer

Author: None (ghostway0)

<details>
<summary>Changes</summary>

fixes #<!-- -->146683 #<!-- -->156829

outputs something like:
==29712==ThreadSanitizer: pthread_detach was called on thread 0 but it is dead.
detach rc1=0 rc2=-22

---
Full diff: https://github.com/llvm/llvm-project/pull/156921.diff


5 Files Affected:

- (modified) compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp (+12-5) 
- (modified) compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h (+1-1) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp (+20-4) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_rtl.h (+1-1) 
- (modified) compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp (+2-2) 


``````````diff
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
index d726d282437ca..b8f32b52c64d8 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
@@ -274,6 +274,11 @@ void ThreadRegistry::JoinThread(u32 tid, void *arg) {
     {
       ThreadRegistryLock l(this);
       ThreadContextBase *tctx = threads_[tid];
+
+      if (!tctx) {
+        Report("%s: Tried to join thread %u, but it is not live.", SanitizerToolName, tid);
+      }
+
       CHECK_NE(tctx, 0);
       if (tctx->status == ThreadStatusInvalid) {
         Report("%s: Join of non-existent thread\n", SanitizerToolName);
@@ -357,17 +362,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..4eabea5addd2a 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -1130,7 +1130,11 @@ 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 +1159,11 @@ 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 +1184,11 @@ 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 +1202,11 @@ 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 {

``````````

</details>


https://github.com/llvm/llvm-project/pull/156921


More information about the llvm-commits mailing list