forked from OSchip/llvm-project
[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:
parent
7d7768e032
commit
43d90cbd86
|
@ -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;
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue