Index: lib/asan/asan_interceptors.cc =================================================================== --- lib/asan/asan_interceptors.cc +++ lib/asan/asan_interceptors.cc @@ -138,6 +138,7 @@ INTERCEPTOR(int, pthread_create, void *thread, void *attr, void *(*start_routine)(void*), void *arg) { + EnsureMainThreadIsCorrect(); // Strict init-order checking in thread-hostile. if (flags()->strict_init_order) StopInitOrderChecking(); Index: lib/asan/asan_thread.h =================================================================== --- lib/asan/asan_thread.h +++ lib/asan/asan_thread.h @@ -124,6 +124,8 @@ u32 GetCurrentTidOrInvalid(); AsanThread *FindThreadByStackAddress(uptr addr); +// Used to handle fork(). +void EnsureMainThreadIsCorrect(); } // namespace __asan #endif // ASAN_THREAD_H Index: lib/asan/asan_thread.cc =================================================================== --- lib/asan/asan_thread.cc +++ lib/asan/asan_thread.cc @@ -254,6 +254,12 @@ (void *)addr)); return tctx ? tctx->thread : 0; } + +void EnsureMainThreadIsCorrect() { + AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet(); + if (context && (context->tid == 0)) + context->os_id = GetTid(); +} } // namespace __asan // --- Implementation of LSan-specific functions --- {{{1 @@ -283,4 +289,8 @@ void UnlockThreadRegistry() { __asan::asanThreadRegistry().Unlock(); } + +void EnsureMainThreadIsCorrect() { + __asan::EnsureMainThreadIsCorrect(); +} } // namespace __lsan Index: lib/lsan/lit_tests/TestCases/fork.cc =================================================================== --- /dev/null +++ lib/lsan/lit_tests/TestCases/fork.cc @@ -0,0 +1,24 @@ +// Test that thread local data is handled correctly after forking without exec(). +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 + +#include +#include +#include +#include +#include + +__thread void *thread_local_var; + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } + return 0; +} Index: lib/lsan/lit_tests/TestCases/fork_threaded.cc =================================================================== --- /dev/null +++ lib/lsan/lit_tests/TestCases/fork_threaded.cc @@ -0,0 +1,44 @@ +// Test that thread local data is handled correctly after forking without +// exec(). In this test leak checking is initiated from a non-main thread. +// RUN: %clangxx_lsan %s -o %t +// RUN: %t 2>&1 + +#include +#include +#include +#include +#include +#include + +__thread void *thread_local_var; + +void *exit_thread_func(void *arg) { + exit(0); +} + +void ExitFromThread() { + pthread_t tid; + int res; + res = pthread_create(&tid, 0, exit_thread_func, 0); + assert(res == 0); + res = pthread_join(tid, 0); + assert(res == 0); +} + +int main() { + int status = 0; + thread_local_var = malloc(1337); + pid_t pid = fork(); + assert(pid >= 0); + if (pid > 0) { + waitpid(pid, &status, 0); + assert(WIFEXITED(status)); + return WEXITSTATUS(status); + } else { + // Spawn a thread and call exit() from there, to check that we track main + // thread's pid correctly even if leak checking is initiated from another + // thread. + ExitFromThread(); + } + return 0; +} Index: lib/lsan/lsan_common.h =================================================================== --- lib/lsan/lsan_common.h +++ lib/lsan/lsan_common.h @@ -135,6 +135,8 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end, uptr *tls_begin, uptr *tls_end, uptr *cache_begin, uptr *cache_end); +// Needed to handle forked processed. +void EnsureMainThreadIsCorrect(); // If p points into a chunk that has been allocated to the user, returns its // user-visible address. Otherwise, returns 0. uptr PointsIntoChunk(void *p); Index: lib/lsan/lsan_common.cc =================================================================== --- lib/lsan/lsan_common.cc +++ lib/lsan/lsan_common.cc @@ -350,6 +350,7 @@ } void DoLeakCheck() { + EnsureMainThreadIsCorrect(); BlockingMutexLock l(&global_mutex); static bool already_done; CHECK(!already_done); Index: lib/lsan/lsan_interceptors.cc =================================================================== --- lib/lsan/lsan_interceptors.cc +++ lib/lsan/lsan_interceptors.cc @@ -218,6 +218,7 @@ INTERCEPTOR(int, pthread_create, void *th, void *attr, void *(*callback)(void *), void *param) { Init(); + EnsureMainThreadIsCorrect(); __sanitizer_pthread_attr_t myattr; if (attr == 0) { pthread_attr_init(&myattr); Index: lib/lsan/lsan_thread.h =================================================================== --- lib/lsan/lsan_thread.h +++ lib/lsan/lsan_thread.h @@ -47,7 +47,7 @@ u32 GetCurrentThread(); void SetCurrentThread(u32 tid); ThreadContext *CurrentThreadContext(); - +void EnsureMainThreadIsCorrect(); } // namespace __lsan #endif // LSAN_THREAD_H Index: lib/lsan/lsan_thread.cc =================================================================== --- lib/lsan/lsan_thread.cc +++ lib/lsan/lsan_thread.cc @@ -123,6 +123,11 @@ thread_registry->JoinThread(tid, /* arg */0); } +void EnsureMainThreadIsCorrect() { + if (GetCurrentThread() == 0) + CurrentThreadContext()->os_id = GetTid(); +} + ///// Interface to the common LSan module. ///// bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,