Skip to content

Commit

Permalink
[lsan] Handle fork() correctly.
Browse files Browse the repository at this point in the history
Update the main thread's os_id on every pthread_create, and before
initiating leak checking. This ensures that we have the correct os_id even if we
have forked after Init().

llvm-svn: 185815
  • Loading branch information
earthdok committed Jul 8, 2013
1 parent 86142e0 commit bdeff95
Show file tree
Hide file tree
Showing 10 changed files with 99 additions and 2 deletions.
1 change: 1 addition & 0 deletions compiler-rt/lib/asan/asan_interceptors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ extern "C" int pthread_attr_getdetachstate(void *attr, int *v);

INTERCEPTOR(int, pthread_create, void *thread,
void *attr, void *(*start_routine)(void*), void *arg) {
EnsureMainThreadIDIsCorrect();
// Strict init-order checking in thread-hostile.
if (flags()->strict_init_order)
StopInitOrderChecking();
Expand Down
14 changes: 13 additions & 1 deletion compiler-rt/lib/asan/asan_thread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,8 @@ static bool ThreadStackContainsAddress(ThreadContextBase *tctx_base,
}

AsanThread *GetCurrentThread() {
AsanThreadContext *context = (AsanThreadContext*)AsanTSDGet();
AsanThreadContext *context =
reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
if (!context) {
if (SANITIZER_ANDROID) {
// On Android, libc constructor is called _after_ asan_init, and cleans up
Expand Down Expand Up @@ -254,6 +255,13 @@ AsanThread *FindThreadByStackAddress(uptr addr) {
(void *)addr));
return tctx ? tctx->thread : 0;
}

void EnsureMainThreadIDIsCorrect() {
AsanThreadContext *context =
reinterpret_cast<AsanThreadContext *>(AsanTSDGet());
if (context && (context->tid == 0))
context->os_id = GetTid();
}
} // namespace __asan

// --- Implementation of LSan-specific functions --- {{{1
Expand Down Expand Up @@ -283,4 +291,8 @@ void LockThreadRegistry() {
void UnlockThreadRegistry() {
__asan::asanThreadRegistry().Unlock();
}

void EnsureMainThreadIDIsCorrect() {
__asan::EnsureMainThreadIDIsCorrect();
}
} // namespace __lsan
2 changes: 2 additions & 0 deletions compiler-rt/lib/asan/asan_thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,8 @@ void SetCurrentThread(AsanThread *t);
u32 GetCurrentTidOrInvalid();
AsanThread *FindThreadByStackAddress(uptr addr);

// Used to handle fork().
void EnsureMainThreadIDIsCorrect();
} // namespace __asan

#endif // ASAN_THREAD_H
24 changes: 24 additions & 0 deletions compiler-rt/lib/lsan/lit_tests/TestCases/fork.cc
Original file line number Diff line number Diff line change
@@ -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 <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

__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;
}
44 changes: 44 additions & 0 deletions compiler-rt/lib/lsan/lit_tests/TestCases/fork_threaded.cc
Original file line number Diff line number Diff line change
@@ -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 <assert.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

__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;
}
1 change: 1 addition & 0 deletions compiler-rt/lib/lsan/lsan_common.cc
Original file line number Diff line number Diff line change
Expand Up @@ -350,6 +350,7 @@ static void DoLeakCheckCallback(const SuspendedThreadsList &suspended_threads,
}

void DoLeakCheck() {
EnsureMainThreadIDIsCorrect();
BlockingMutexLock l(&global_mutex);
static bool already_done;
CHECK(!already_done);
Expand Down
7 changes: 7 additions & 0 deletions compiler-rt/lib/lsan/lsan_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,13 @@ void UnlockThreadRegistry();
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
uptr *tls_begin, uptr *tls_end,
uptr *cache_begin, uptr *cache_end);
// If called from the main thread, updates the main thread's TID in the thread
// registry. We need this to handle processes that fork() without a subsequent
// exec(), which invalidates the recorded TID. To update it, we must call
// gettid() from the main thread. Our solution is to call this function before
// leak checking and also before every call to pthread_create() (to handle cases
// where leak checking is initiated from a non-main thread).
void EnsureMainThreadIDIsCorrect();
// 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);
Expand Down
1 change: 1 addition & 0 deletions compiler-rt/lib/lsan/lsan_interceptors.cc
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,7 @@ extern "C" void *__lsan_thread_start_func(void *arg) {
INTERCEPTOR(int, pthread_create, void *th, void *attr,
void *(*callback)(void *), void *param) {
Init();
EnsureMainThreadIDIsCorrect();
__sanitizer_pthread_attr_t myattr;
if (attr == 0) {
pthread_attr_init(&myattr);
Expand Down
5 changes: 5 additions & 0 deletions compiler-rt/lib/lsan/lsan_thread.cc
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,11 @@ void ThreadJoin(u32 tid) {
thread_registry->JoinThread(tid, /* arg */0);
}

void EnsureMainThreadIDIsCorrect() {
if (GetCurrentThread() == 0)
CurrentThreadContext()->os_id = GetTid();
}

///// Interface to the common LSan module. /////

bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/lsan/lsan_thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ u32 ThreadTid(uptr uid);
u32 GetCurrentThread();
void SetCurrentThread(u32 tid);
ThreadContext *CurrentThreadContext();

void EnsureMainThreadIDIsCorrect();
} // namespace __lsan

#endif // LSAN_THREAD_H

0 comments on commit bdeff95

Please sign in to comment.