[lsan] Support ASan's stack-use-after-return mode in LSan.

Treat the fake stack as live memory.

llvm-svn: 192593
This commit is contained in:
Sergey Matveev 2013-10-14 14:04:50 +00:00
parent 7d7768e032
commit 43d90cbd86
8 changed files with 69 additions and 4 deletions

View File

@ -134,6 +134,20 @@ NOINLINE void FakeStack::GC(uptr real_stack) {
needs_gc_ = false;
}
void FakeStack::ForEachFakeFrame(RangeIteratorCallback callback, void *arg) {
for (uptr class_id = 0; class_id < kNumberOfSizeClasses; class_id++) {
u8 *flags = GetFlags(stack_size_log(), class_id);
for (uptr i = 0, n = NumberOfFrames(stack_size_log(), class_id); i < n;
i++) {
if (flags[i] == 0) continue; // not allocated.
FakeFrame *ff = reinterpret_cast<FakeFrame *>(
GetFrame(stack_size_log(), class_id, i));
uptr begin = reinterpret_cast<uptr>(ff);
callback(begin, begin + FakeStack::BytesInSizeClass(class_id), arg);
}
}
}
#if SANITIZER_LINUX && !SANITIZER_ANDROID
static THREADLOCAL FakeStack *fake_stack_tls;

View File

@ -148,6 +148,8 @@ class FakeStack {
void HandleNoReturn();
void GC(uptr real_stack);
void ForEachFakeFrame(RangeIteratorCallback callback, void *arg);
private:
FakeStack() { }
static const uptr kFlagsOffset = 4096; // This is were the flags begin.

View File

@ -294,6 +294,13 @@ void EnsureMainThreadIDIsCorrect() {
if (context && (context->tid == 0))
context->os_id = GetTid();
}
__asan::AsanThread *GetAsanThreadByOsIDLocked(uptr os_id) {
__asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
__asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
if (!context) return 0;
return context->thread;
}
} // namespace __asan
// --- Implementation of LSan-specific functions --- {{{1
@ -301,10 +308,7 @@ namespace __lsan {
bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
uptr *tls_begin, uptr *tls_end,
uptr *cache_begin, uptr *cache_end) {
__asan::AsanThreadContext *context = static_cast<__asan::AsanThreadContext *>(
__asan::asanThreadRegistry().FindThreadContextByOsIDLocked(os_id));
if (!context) return false;
__asan::AsanThread *t = context->thread;
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
if (!t) return false;
*stack_begin = t->stack_bottom();
*stack_end = t->stack_top();
@ -316,6 +320,13 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
return true;
}
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
void *arg) {
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
if (t && t->has_fake_stack())
t->fake_stack()->ForEachFakeFrame(callback, arg);
}
void LockThreadRegistry() {
__asan::asanThreadRegistry().Lock();
}

View File

@ -0,0 +1,23 @@
// Test that fake stack (introduced by ASan's use-after-return mode) is included
// in the root set.
// RUN: LSAN_BASE="report_objects=1:use_registers=0"
// RUN: %clangxx_lsan %s -O2 -o %t
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=0" not %t 2>&1 | FileCheck %s
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE:"use_stacks=1" %t 2>&1
// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS="" %t 2>&1
#include <stdio.h>
#include <stdlib.h>
int main() {
void *stack_var = malloc(1337);
fprintf(stderr, "Test alloc: %p.\n", stack_var);
// Take pointer to variable, to ensure it's not optimized into a register.
fprintf(stderr, "Stack var at: %p.\n", &stack_var);
// Do not return from main to prevent the pointer from going out of scope.
exit(0);
}
// CHECK: Test alloc: [[ADDR:.*]].
// CHECK: Directly leaked 1337 byte object at [[ADDR]]
// CHECK: LeakSanitizer: detected memory leaks
// CHECK: SUMMARY: LeakSanitizer:

View File

@ -151,6 +151,11 @@ void ScanRangeForPointers(uptr begin, uptr end,
}
}
void ForEachExtraStackRangeCb(uptr begin, uptr end, void* arg) {
Frontier *frontier = reinterpret_cast<Frontier *>(arg);
ScanRangeForPointers(begin, end, frontier, "FAKE STACK", kReachable);
}
// Scans thread data (stacks and TLS) for heap pointers.
static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
Frontier *frontier) {
@ -199,6 +204,7 @@ static void ProcessThreads(SuspendedThreadsList const &suspended_threads,
}
ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
kReachable);
ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
}
if (flags()->use_tls) {

View File

@ -135,6 +135,8 @@ 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);
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
void *arg);
// 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

View File

@ -145,6 +145,10 @@ bool GetThreadRangesLocked(uptr os_id, uptr *stack_begin, uptr *stack_end,
return true;
}
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
void *arg) {
}
void LockThreadRegistry() {
thread_registry->Lock();
}

View File

@ -446,6 +446,9 @@ const uptr kPthreadDestructorIterations = 4;
// Unused on Windows.
const uptr kPthreadDestructorIterations = 0;
#endif
// Callback type for iterating over a set of memory ranges.
typedef void (*RangeIteratorCallback)(uptr begin, uptr end, void *arg);
} // namespace __sanitizer
#endif // SANITIZER_COMMON_H