[compiler-rt] 5e40661 - [sanitizer] Fix asserts in asan and tsan in pthread interceptors. (#75394)
via llvm-commits
llvm-commits at lists.llvm.org
Thu Jan 11 04:23:37 PST 2024
Author: goussepi
Date: 2024-01-11T12:23:33Z
New Revision: 5e406615fea185656786e8a5e72b6f12fd7706d5
URL: https://github.com/llvm/llvm-project/commit/5e406615fea185656786e8a5e72b6f12fd7706d5
DIFF: https://github.com/llvm/llvm-project/commit/5e406615fea185656786e8a5e72b6f12fd7706d5.diff
LOG: [sanitizer] Fix asserts in asan and tsan in pthread interceptors. (#75394)
Calling one of pthread join/detach interceptor on an already
joined/detached thread causes asserts such as:
AddressSanitizer: CHECK failed: sanitizer_thread_arg_retval.cpp:56
"((t)) != (0)" (0x0, 0x0) (tid=1236094)
#0 0x555555634f8b in __asan::CheckUnwind()
compiler-rt/lib/asan/asan_rtl.cpp:69:3
#1 0x55555564e06e in __sanitizer::CheckFailed(char const*, int, char
const*, unsigned long long, unsigned long long)
compiler-rt/lib/sanitizer_common/sanitizer_termination.cpp:86:24
#2 0x5555556491df in __sanitizer::ThreadArgRetval::BeforeJoin(unsigned
long) const
compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp:56:3
#3 0x5555556198ed in Join<___interceptor_pthread_tryjoin_np(void*,
void**)::<lambda()> >
compiler-rt/lib/asan/../sanitizer_common/sanitizer_thread_arg_retval.h:74:26
#4 0x5555556198ed in pthread_tryjoin_np
compiler-rt/lib/asan/asan_interceptors.cpp:311:29
The assert are replaced by error codes.
Added:
compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join_invalid.cpp
Modified:
compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp
compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h
compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join.cpp
Removed:
################################################################################
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
index 7836347d233add..c1e3530618c20d 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_flags.inc
@@ -279,3 +279,6 @@ COMMON_FLAG(bool, test_only_replace_dlopen_main_program, false,
COMMON_FLAG(bool, enable_symbolizer_markup, SANITIZER_FUCHSIA,
"Use sanitizer symbolizer markup, available on Linux "
"and always set true for Fuchsia.")
+
+COMMON_FLAG(bool, detect_invalid_join, true,
+ "If set, check invalid joins of threads.")
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp b/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp
index bddb2852140854..754fd7b65a1dd9 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.cpp
@@ -23,6 +23,9 @@ void ThreadArgRetval::CreateLocked(uptr thread, bool detached,
Data& t = data_[thread];
t = {};
t.gen = gen_++;
+ static_assert(sizeof(gen_) == sizeof(u32) && kInvalidGen == UINT32_MAX);
+ if (gen_ == kInvalidGen)
+ gen_ = 0;
t.detached = detached;
t.args = args;
}
@@ -53,16 +56,28 @@ void ThreadArgRetval::Finish(uptr thread, void* retval) {
u32 ThreadArgRetval::BeforeJoin(uptr thread) const {
__sanitizer::Lock lock(&mtx_);
auto t = data_.find(thread);
- CHECK(t);
- CHECK(!t->second.detached);
- return t->second.gen;
+ if (t && !t->second.detached) {
+ return t->second.gen;
+ }
+ if (!common_flags()->detect_invalid_join)
+ return kInvalidGen;
+ const char* reason = "unknown";
+ if (!t) {
+ reason = "already joined";
+ } else if (t->second.detached) {
+ reason = "detached";
+ }
+ Report("ERROR: %s: Joining %s thread, aborting.\n", SanitizerToolName,
+ reason);
+ Die();
}
void ThreadArgRetval::AfterJoin(uptr thread, u32 gen) {
__sanitizer::Lock lock(&mtx_);
auto t = data_.find(thread);
if (!t || gen != t->second.gen) {
- // Thread was reused and erased by any other event.
+ // Thread was reused and erased by any other event, or we had an invalid
+ // join.
return;
}
CHECK(!t->second.detached);
diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h b/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h
index c77021beb67d12..0e6d35131c23fc 100644
--- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h
+++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_arg_retval.h
@@ -93,6 +93,7 @@ class SANITIZER_MUTEX ThreadArgRetval {
// will keep pointers alive forever, missing leaks caused by cancelation.
private:
+ static const u32 kInvalidGen = UINT32_MAX;
struct Data {
Args args;
u32 gen; // Avoid collision if thread id re-used.
diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join.cpp
index 212a28dd3985bb..d0d69bccf08224 100644
--- a/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join.cpp
+++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join.cpp
@@ -1,13 +1,14 @@
// RUN: %clangxx -pthread %s -o %t
-// RUN: %run %t 0
+// RUN: %env_tool_opts=detect_invalid_join=false %run %t 0
// FIXME: Crashes on some bots in pthread_exit.
-// RUN: %run %t %if tsan %{ 0 %} %else %{ 1 %}
+// RUN: %env_tool_opts=detect_invalid_join=false %run %t %if tsan %{ 0 %} %else %{ 1 %}
// REQUIRES: glibc
#include <assert.h>
#include <ctime>
+#include <errno.h>
#include <pthread.h>
#include <stdint.h>
#include <stdlib.h>
@@ -24,6 +25,7 @@ static void *fn(void *args) {
int main(int argc, char **argv) {
use_exit = atoi(argv[1]);
+ bool check_invalid_join = !use_exit;
pthread_t thread[5];
assert(!pthread_create(&thread[0], nullptr, fn, (void *)1000));
assert(!pthread_create(&thread[1], nullptr, fn, (void *)1001));
@@ -42,6 +44,8 @@ int main(int argc, char **argv) {
while (pthread_tryjoin_np(thread[1], &res))
sleep(1);
assert(~(uintptr_t)res == 1001);
+ assert(check_invalid_join ||
+ (pthread_tryjoin_np(thread[1], &res) == EBUSY));
}
{
@@ -50,12 +54,15 @@ int main(int argc, char **argv) {
while (pthread_timedjoin_np(thread[2], &res, &tm))
sleep(1);
assert(~(uintptr_t)res == 1002);
+ assert(check_invalid_join ||
+ (pthread_timedjoin_np(thread[2], &res, &tm) == ESRCH));
}
{
void *res;
assert(!pthread_join(thread[3], &res));
assert(~(uintptr_t)res == 1003);
+ assert(check_invalid_join || (pthread_join(thread[3], &res) == ESRCH));
}
return 0;
diff --git a/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join_invalid.cpp b/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join_invalid.cpp
new file mode 100644
index 00000000000000..59fe7bb65cdc14
--- /dev/null
+++ b/compiler-rt/test/sanitizer_common/TestCases/Linux/pthread_join_invalid.cpp
@@ -0,0 +1,47 @@
+// RUN: %clangxx -pthread %s -o %t
+
+// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 0 2>&1 | FileCheck %s
+// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 1 2>&1 | FileCheck %s
+// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 2 2>&1 | FileCheck %s
+// RUN: %env_tool_opts=detect_invalid_join=true not %run %t 3 2>&1 | FileCheck %s --check-prefix=DETACH
+
+// REQUIRES: glibc && (asan || hwasan || lsan)
+
+#include <assert.h>
+#include <ctime>
+#include <errno.h>
+#include <pthread.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+static void *fn(void *args) {
+ sleep(1);
+ return nullptr;
+}
+
+int main(int argc, char **argv) {
+ int n = atoi(argv[1]);
+ pthread_t thread;
+ assert(!pthread_create(&thread, nullptr, fn, nullptr));
+ void *res;
+ if (n == 0) {
+ while (pthread_tryjoin_np(thread, &res))
+ sleep(1);
+ pthread_tryjoin_np(thread, &res);
+ } else if (n == 1) {
+ timespec tm = {0, 1};
+ while (pthread_timedjoin_np(thread, &res, &tm))
+ sleep(1);
+ pthread_timedjoin_np(thread, &res, &tm);
+ } else if (n == 2) {
+ assert(!pthread_join(thread, &res));
+ pthread_join(thread, &res);
+ } else if (n == 3) {
+ assert(!pthread_detach(thread));
+ pthread_join(thread, &res);
+ }
+ // CHECK: Joining already joined thread
+ // DETACH: Joining detached thread
+ return 0;
+}
More information about the llvm-commits
mailing list