diff --git a/compiler-rt/lib/lsan/lsan_thread.cpp b/compiler-rt/lib/lsan/lsan_thread.cpp --- a/compiler-rt/lib/lsan/lsan_thread.cpp +++ b/compiler-rt/lib/lsan/lsan_thread.cpp @@ -60,12 +60,18 @@ } ThreadContext *CurrentThreadContext() { - if (!thread_registry) - return nullptr; - if (GetCurrentThread() == kInvalidTid) - return nullptr; - // No lock needed when getting current thread. - return (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); + static __attribute__((tls_model("initial-exec"))) + THREADLOCAL ThreadContext *current_thread_context; + if (!current_thread_context) { + if (!thread_registry) + return nullptr; + if (GetCurrentThread() == kInvalidTid) + return nullptr; + ThreadRegistryLock trl(thread_registry); + current_thread_context = + (ThreadContext *)thread_registry->GetThreadLocked(GetCurrentThread()); + } + return current_thread_context; } void EnsureMainThreadIDIsCorrect() { diff --git a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h --- a/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h +++ b/compiler-rt/lib/sanitizer_common/sanitizer_thread_registry.h @@ -101,7 +101,7 @@ // Should be guarded by ThreadRegistryLock. ThreadContextBase *GetThreadLocked(u32 tid) { - return threads_.empty() ? nullptr : threads_[tid]; + return tid < threads_.size() ? threads_[tid] : nullptr; } u32 NumThreadsLocked() const { return threads_.size(); } diff --git a/compiler-rt/test/lsan/TestCases/thread_context_crash.cpp b/compiler-rt/test/lsan/TestCases/thread_context_crash.cpp new file mode 100644 --- /dev/null +++ b/compiler-rt/test/lsan/TestCases/thread_context_crash.cpp @@ -0,0 +1,33 @@ +// Check that concurent CurrentThreadContext does not crash. +// RUN: %clangxx_lsan -O3 -pthread %s -o %t && %run %t 100 + +// REQUIRES: lsan-standalone + +#include +#include +#include + +#include + +namespace __lsan { +class ThreadContext *CurrentThreadContext(); +} + +void *null_func(void *args) { + for (int i = 0; i < 100000; ++i) + __lsan::CurrentThreadContext(); + return nullptr; +} + +int main(int argc, char **argv) { + std::vector threads; + for (int i = 0; i < atoi(argv[1]); ++i) { + threads.resize(10); + for (auto &thread : threads) + pthread_create(&thread, 0, null_func, NULL); + + for (auto &thread : threads) + pthread_join(thread, nullptr); + } + return 0; +} diff --git a/compiler-rt/test/lsan/lit.common.cfg.py b/compiler-rt/test/lsan/lit.common.cfg.py --- a/compiler-rt/test/lsan/lit.common.cfg.py +++ b/compiler-rt/test/lsan/lit.common.cfg.py @@ -26,6 +26,7 @@ if lsan_lit_test_mode == "Standalone": config.name = "LeakSanitizer-Standalone" lsan_cflags = ["-fsanitize=leak"] + config.available_features.add('lsan-standalone') elif lsan_lit_test_mode == "AddressSanitizer": config.name = "LeakSanitizer-AddressSanitizer" lsan_cflags = ["-fsanitize=address"]