[PATCH] [asan] Wipe all poisoned memory in fake stack frames before passing them to LSan
Sergey Matveev
earthdok at google.com
Thu Oct 17 12:58:21 PDT 2013
- added test
Hi kcc,
http://llvm-reviews.chandlerc.com/D1962
CHANGE SINCE LAST DIFF
http://llvm-reviews.chandlerc.com/D1962?vs=5005&id=5007#toc
Files:
lib/asan/asan_fake_stack.cc
lib/asan/asan_fake_stack.h
lib/asan/asan_thread.cc
lib/lsan/lit_tests/TestCases/fake_stack_redzone.cc
lib/lsan/lsan_common.cc
lib/lsan/lsan_common.h
Index: lib/asan/asan_fake_stack.cc
===================================================================
--- lib/asan/asan_fake_stack.cc
+++ lib/asan/asan_fake_stack.cc
@@ -148,6 +148,30 @@
}
}
+void FakeStack::WipePoisonedCallback(uptr begin, uptr end, void *arg) {
+ uptr ptr = begin + sizeof(FakeFrame);
+ CHECK_EQ(0, ptr % 8);
+ CHECK_EQ(0, end % 8);
+ u8 *shadow_ptr = (u8 *)MemToShadow(ptr);
+ uptr poisoned_begin = 0;
+ while (ptr < end) {
+ u8 shadow = *shadow_ptr;
+ if (poisoned_begin > 0) {
+ if (shadow < 8) {
+ internal_memset((void *)poisoned_begin, 0, ptr - poisoned_begin);
+ poisoned_begin = (shadow > 0) ? (ptr + shadow) : 0;
+ }
+ } else {
+ if (shadow)
+ poisoned_begin = ptr + ((shadow < 8) ? shadow : 0);
+ }
+ shadow_ptr++;
+ ptr += 8;
+ }
+ if (poisoned_begin > 0)
+ internal_memset((void *)poisoned_begin, 0, end - poisoned_begin);
+}
+
#if SANITIZER_LINUX && !SANITIZER_ANDROID
static THREADLOCAL FakeStack *fake_stack_tls;
Index: lib/asan/asan_fake_stack.h
===================================================================
--- lib/asan/asan_fake_stack.h
+++ lib/asan/asan_fake_stack.h
@@ -149,6 +149,7 @@
void GC(uptr real_stack);
void ForEachFakeFrame(RangeIteratorCallback callback, void *arg);
+ static void WipePoisonedCallback(uptr begin, uptr end, void *arg);
private:
FakeStack() { }
Index: lib/asan/asan_thread.cc
===================================================================
--- lib/asan/asan_thread.cc
+++ lib/asan/asan_thread.cc
@@ -323,8 +323,13 @@
void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
void *arg) {
__asan::AsanThread *t = __asan::GetAsanThreadByOsIDLocked(os_id);
- if (t && t->has_fake_stack())
+ if (t && t->has_fake_stack()) {
+ // Overwrite all poisoned memory ranges with zeros. This should help
+ // against garbage pointers contained in reused stack frames.
+ t->fake_stack()->ForEachFakeFrame(&__asan::FakeStack::WipePoisonedCallback,
+ 0);
t->fake_stack()->ForEachFakeFrame(callback, arg);
+ }
}
void LockThreadRegistry() {
Index: lib/lsan/lit_tests/TestCases/fake_stack_redzone.cc
===================================================================
--- /dev/null
+++ lib/lsan/lit_tests/TestCases/fake_stack_redzone.cc
@@ -0,0 +1,42 @@
+// When UAR is enabled in ASan, we should ignore pointers which are found in
+// active stack frames but outside valid variables (i.e. in redzones or the
+// unused space in the fake frame).
+// Under standalone LSan this test passes trivially, since real stacks are
+// disabled.
+// RUN: LSAN_BASE="report_objects=1:use_registers=0:use_only_fake_stack=1"
+// RUN: %clangxx_lsan %s -O0 -o %t
+// RUN: ASAN_OPTIONS=$ASAN_OPTIONS:detect_stack_use_after_return=1 LSAN_OPTIONS=$LSAN_BASE not %t 2>&1 | FileCheck %s
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include <sanitizer/lsan_interface.h>
+
+void *p;
+
+__attribute__((noinline))
+void PutPointerInRedzone() {
+ volatile void *arr[2];
+ arr[1] = p;
+}
+
+__attribute__((noinline))
+void DoLeakCheck() {
+ volatile void *q;
+ // Now the pointer should be in q's right redzone.
+ __lsan_do_leak_check();
+}
+
+int main() {
+ p = malloc(1337);
+ fprintf(stderr, "Test alloc: %p.\n", p);
+ // Cycle through the fake frame pool.
+ for (int i = 0; i < 1000000; i++)
+ PutPointerInRedzone();
+ p = 0;
+ DoLeakCheck();
+}
+// CHECK: Test alloc: [[ADDR:.*]].
+// CHECK: Directly leaked 1337 byte object at [[ADDR]]
+// CHECK: LeakSanitizer: detected memory leaks
+// CHECK: SUMMARY: LeakSanitizer:
Index: lib/lsan/lsan_common.cc
===================================================================
--- lib/lsan/lsan_common.cc
+++ lib/lsan/lsan_common.cc
@@ -45,6 +45,7 @@
f->use_registers = true;
f->use_globals = true;
f->use_stacks = true;
+ f->use_only_fake_stack = false;
f->use_tls = true;
f->use_unaligned = false;
f->verbosity = 0;
@@ -56,6 +57,7 @@
ParseFlag(options, &f->use_registers, "use_registers");
ParseFlag(options, &f->use_globals, "use_globals");
ParseFlag(options, &f->use_stacks, "use_stacks");
+ ParseFlag(options, &f->use_only_fake_stack, "use_only_fake_stack");
ParseFlag(options, &f->use_tls, "use_tls");
ParseFlag(options, &f->use_unaligned, "use_unaligned");
ParseFlag(options, &f->report_objects, "report_objects");
@@ -191,7 +193,7 @@
ScanRangeForPointers(registers_begin, registers_end, frontier,
"REGISTERS", kReachable);
- if (flags()->use_stacks) {
+ if (flags()->use_stacks && !flags()->use_only_fake_stack) {
if (flags()->log_threads)
Report("Stack at %p-%p, SP = %p.\n", stack_begin, stack_end, sp);
if (sp < stack_begin || sp >= stack_end) {
@@ -206,8 +208,10 @@
}
ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
kReachable);
- ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
}
+ // Fake stack.
+ if (flags()->use_stacks)
+ ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
if (flags()->use_tls) {
if (flags()->log_threads) Report("TLS at %p-%p.\n", tls_begin, tls_end);
Index: lib/lsan/lsan_common.h
===================================================================
--- lib/lsan/lsan_common.h
+++ lib/lsan/lsan_common.h
@@ -59,6 +59,8 @@
bool use_globals;
// Thread stacks.
bool use_stacks;
+ // Only use ASan's fake stack, ignoring the real stack. Requires use_stacks.
+ bool use_only_fake_stack;
// Thread registers.
bool use_registers;
// TLS and thread-specific storage.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D1962.2.patch
Type: text/x-patch
Size: 5751 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20131017/38af49e1/attachment.bin>
More information about the llvm-commits
mailing list