[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. llvm-svn: 192585
This commit is contained in:
parent
7aacd9c172
commit
da9f5e7e7f
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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 <assert.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#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]]
|
Loading…
Reference in New Issue