From e86e35fbe861e73c5991200510a028877427b3e7 Mon Sep 17 00:00:00 2001 From: Sergey Matveev Date: Mon, 14 Oct 2013 12:01:05 +0000 Subject: [asan] Improve thread lifetime tracking on POSIX systems. Call AsanThread::Destroy() from a late-running TSD destructor. Previously we called it before any user-registered TSD destructors, which caused false positives in LeakSanitizer. git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@192585 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/asan/asan_internal.h | 1 + lib/asan/asan_posix.cc | 11 +++++- lib/asan/asan_rtl.cc | 2 +- lib/asan/asan_thread.cc | 8 +++- lib/asan/asan_thread.h | 3 ++ lib/asan/asan_win.cc | 3 ++ .../TestCases/cleanup_in_tsd_destructor.cc | 45 ++++++++++++++++++++++ 7 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h index 7a4d7447..70e55ea0 100644 --- a/lib/asan/asan_internal.h +++ b/lib/asan/asan_internal.h @@ -98,6 +98,7 @@ void StopInitOrderChecking(); void AsanTSDInit(void (*destructor)(void *tsd)); void *AsanTSDGet(); void AsanTSDSet(void *tsd); +void PlatformTSDDtor(void *tsd); void AppendToErrorMessageBuffer(const char *buffer); diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc index 5126a756..1926fccb 100644 --- a/lib/asan/asan_posix.cc +++ b/lib/asan/asan_posix.cc @@ -1,4 +1,4 @@ -//===-- asan_linux.cc -----------------------------------------------------===// +//===-- asan_posix.cc -----------------------------------------------------===// // // The LLVM Compiler Infrastructure // @@ -116,6 +116,15 @@ void AsanTSDSet(void *tsd) { pthread_setspecific(tsd_key, tsd); } +void PlatformTSDDtor(void *tsd) { + AsanThreadContext *context = (AsanThreadContext*)tsd; + if (context->destructor_iterations > 1) { + context->destructor_iterations--; + CHECK_EQ(0, pthread_setspecific(tsd_key, tsd)); + return; + } + AsanThread::TSDDtor(tsd); +} } // namespace __asan #endif // SANITIZER_LINUX || SANITIZER_MAC diff --git a/lib/asan/asan_rtl.cc b/lib/asan/asan_rtl.cc index f0b31f05..d93764ce 100644 --- a/lib/asan/asan_rtl.cc +++ b/lib/asan/asan_rtl.cc @@ -535,7 +535,7 @@ void __asan_init() { InstallSignalHandlers(); - AsanTSDInit(AsanThread::TSDDtor); + AsanTSDInit(PlatformTSDDtor); // Allocator should be initialized before starting external symbolizer, as // fork() on Mac locks the allocator. InitializeAllocator(); diff --git a/lib/asan/asan_thread.cc b/lib/asan/asan_thread.cc index 5a81a5e0..1949cd83 100644 --- a/lib/asan/asan_thread.cc +++ b/lib/asan/asan_thread.cc @@ -165,7 +165,13 @@ thread_return_t AsanThread::ThreadStart(uptr os_id) { malloc_storage().CommitBack(); if (flags()->use_sigaltstack) UnsetAlternateSignalStack(); - this->Destroy(); + // On POSIX systems we defer this to the TSD destructor. LSan will consider + // the thread's memory as non-live from the moment we call Destroy(), even + // though that memory might contain pointers to heap objects which will be + // cleaned up by a user-defined TSD destructor. Thus, calling Destroy() before + // the TSD destructors have run might cause false positives in LSan. + if (!SANITIZER_POSIX) + this->Destroy(); return res; } diff --git a/lib/asan/asan_thread.h b/lib/asan/asan_thread.h index e77f16c6..5d0a6c94 100644 --- a/lib/asan/asan_thread.h +++ b/lib/asan/asan_thread.h @@ -19,6 +19,7 @@ #include "asan_fake_stack.h" #include "asan_stack.h" #include "asan_stats.h" +#include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_thread_registry.h" @@ -36,10 +37,12 @@ class AsanThreadContext : public ThreadContextBase { explicit AsanThreadContext(int tid) : ThreadContextBase(tid), announced(false), + destructor_iterations(kPthreadDestructorIterations), thread(0) { internal_memset(&stack, 0, sizeof(stack)); } bool announced; + int destructor_iterations; StackTrace stack; AsanThread *thread; diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc index 8f35e0b9..9e66b341 100644 --- a/lib/asan/asan_win.cc +++ b/lib/asan/asan_win.cc @@ -60,6 +60,9 @@ void AsanTSDSet(void *tsd) { fake_tsd = tsd; } +void PlatformTSDDtor(void *tsd) { + AsanThread::TSDDtor(tsd); +} // ---------------------- Various stuff ---------------- {{{1 void MaybeReexec() { // No need to re-exec on Windows. diff --git a/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc b/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc new file mode 100644 index 00000000..ab368245 --- /dev/null +++ b/lib/lsan/lit_tests/TestCases/cleanup_in_tsd_destructor.cc @@ -0,0 +1,45 @@ +// Regression test for thread lifetime tracking. Thread data should be +// considered live during the thread's termination, at least until the +// user-installed TSD destructors have finished running (since they may contain +// additional cleanup tasks). LSan doesn't actually meet that goal 100%, but it +// makes its best effort. +// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_stacks=0:use_globals=0" +// RUN: %clangxx_lsan %s -o %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=1 %t +// RUN: LSAN_OPTIONS=$LSAN_BASE:use_tls=0 not %t 2>&1 | FileCheck %s + +#include +#include +#include +#include + +#include "sanitizer/lsan_interface.h" + +pthread_key_t key; +__thread void *p; + +void key_destructor(void *arg) { + // Generally this may happen on a different thread. + __lsan_do_leak_check(); +} + +void *thread_func(void *arg) { + p = malloc(1337); + fprintf(stderr, "Test alloc: %p.\n", p); + int res = pthread_setspecific(key, (void*)1); + assert(res == 0); + return 0; +} + +int main() { + int res = pthread_key_create(&key, &key_destructor); + assert(res == 0); + pthread_t thread_id; + res = pthread_create(&thread_id, 0, thread_func, 0); + assert(res == 0); + res = pthread_join(thread_id, 0); + assert(res == 0); + return 0; +} +// CHECK: Test alloc: [[ADDR:.*]]. +// CHECK: leaked 1337 byte object at [[ADDR]] -- cgit v1.2.3