[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