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 +#include + +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 +#include + +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.