-
Notifications
You must be signed in to change notification settings - Fork 14.9k
TSAN: Report when thread is not live and referenced in pthread #156921
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Thank you for submitting a Pull Request (PR) to the LLVM Project! This PR will be automatically labeled and the relevant teams will be notified. If you wish to, you can add reviewers by using the "Reviewers" section on this page. If this is not working for you, it is probably because you do not have write permissions for the repository. In which case you can instead tag reviewers by name in a comment by using If you have received no comments on your PR for a week, you can request a review by "ping"ing the PR by adding a comment “Ping”. The common courtesy "ping" rate is once a week. Please remember that you are asking for valuable time from other developers. If you have further questions, they may be answered by the LLVM GitHub User Guide. You can also ask questions in a comment on this PR, on the LLVM Discord or on the forums. |
@llvm/pr-subscribers-compiler-rt-sanitizer Author: None (ghostway0) Changesfixes #146683 #156829 outputs something like: Full diff: https://github.com/llvm/llvm-project/pull/156921.diff 5 Files Affected:
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 {
|
You can test this locally with the following command:git-clang-format --diff origin/main HEAD --extensions cpp,h -- compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp compiler-rt/lib/tsan/rtl/tsan_rtl.h compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
View the diff from clang-format here.diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
index b8f32b52c..aaac2e852 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.cpp
@@ -276,7 +276,8 @@ void ThreadRegistry::JoinThread(u32 tid, void *arg) {
ThreadContextBase *tctx = threads_[tid];
if (!tctx) {
- Report("%s: Tried to join thread %u, but it is not live.", SanitizerToolName, tid);
+ Report("%s: Tried to join thread %u, but it is not live.",
+ SanitizerToolName, tid);
}
CHECK_NE(tctx, 0);
@@ -362,7 +363,7 @@ ThreadContextBase *ThreadRegistry::QuarantinePop() {
return tctx;
}
-bool ThreadRegistry::ConsumeThreadUserId(uptr user_id, u32 *tid_out) {
+bool ThreadRegistry::ConsumeThreadUserId(uptr user_id, u32* tid_out) {
ThreadRegistryLock l(this);
auto *t = live_.find(user_id);
if (!t) {
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h
index 83459685f..e4398f323 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);
- bool ConsumeThreadUserId(uptr user_id, u32 *tid_out);
+ 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 4eabea5ad..aa5f4e2d1 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp
@@ -1132,7 +1132,10 @@ TSAN_INTERCEPTOR(int, pthread_join, void *th, void **ret) {
SCOPED_INTERCEPTOR_RAW(pthread_join, th, ret);
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);
+ Report(
+ "ThreadSanitizer: pthread_join was called on thread %d but it is "
+ "dead.\n",
+ thr->tid);
return -errno_EINVAL;
}
ThreadIgnoreBegin(thr, pc);
@@ -1161,7 +1164,10 @@ TSAN_INTERCEPTOR(int, pthread_detach, void *th) {
SCOPED_INTERCEPTOR_RAW(pthread_detach, 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);
+ 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);
@@ -1186,7 +1192,10 @@ TSAN_INTERCEPTOR(int, pthread_tryjoin_np, void *th, void **ret) {
SCOPED_INTERCEPTOR_RAW(pthread_tryjoin_np, th, ret);
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);
+ Report(
+ "ThreadSanitizer: pthread_tryjoin_np was called on thread %d but it is "
+ "dead.\n",
+ thr->tid);
return -errno_EINVAL;
}
ThreadIgnoreBegin(thr, pc);
@@ -1204,7 +1213,10 @@ TSAN_INTERCEPTOR(int, pthread_timedjoin_np, void *th, void **ret,
SCOPED_INTERCEPTOR_RAW(pthread_timedjoin_np, th, ret, abstime);
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);
+ Report(
+ "ThreadSanitizer: pthread_timedjoin_np was called on thread %d but it "
+ "is dead.\n",
+ thr->tid);
return -errno_EINVAL;
}
ThreadIgnoreBegin(thr, pc);
diff --git a/compiler-rt/lib/tsan/rtl/tsan_rtl.h b/compiler-rt/lib/tsan/rtl/tsan_rtl.h
index 03bd4d8de..ea0b2c163 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);
-bool ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid, Tid *tid_out);
+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 fd33dde7e..a8cb1d385 100644
--- a/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
+++ b/compiler-rt/lib/tsan/rtl/tsan_rtl_thread.cpp
@@ -301,7 +301,7 @@ struct ConsumeThreadContext {
ThreadContextBase *tctx;
};
-bool ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid, Tid *tid_out) {
+bool ThreadConsumeTid(ThreadState* thr, uptr pc, uptr uid, Tid* tid_out) {
return ctx->thread_registry.ConsumeThreadUserId(uid, tid_out);
}
|
311b187
to
f346915
Compare
|
||
int main() { | ||
pthread_t th; | ||
int rc = pthread_create(&th, 0, fn, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Author of TSAN @dvyukov previously expressed strong opinion that it UB and tsan should crash here, and test above.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
what's UB here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should only be UB if not linked with -pthread? or am i wrong?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pthread_detach twice
and detach joined thread
f346915
to
2b5dbfd
Compare
fixes #146683 #156829
When a process calls pthread_{join,detach} twice, TSAN crashes when it doesn't find the current thread to remove from the live threads map.
This patch makes it output something like:
==29712==ThreadSanitizer: pthread_detach was called on thread 0 but it is dead.