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

Sergey Matveev earthdok at google.com
Mon Oct 14 07:03:00 PDT 2013


    - fix one more use of ForEachFakeFrame

Hi kcc, samsonov,

http://llvm-reviews.chandlerc.com/D1901

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1901?vs=4894&id=4895#toc

Files:
  lib/asan/asan_fake_stack.cc
  lib/asan/asan_fake_stack.h
  lib/asan/asan_thread.cc
  lib/lsan/lit_tests/TestCases/use_after_return.cc
  lib/lsan/lsan_common.cc
  lib/lsan/lsan_common.h
  lib/lsan/lsan_thread.cc
  lib/sanitizer_common/sanitizer_common.h

Index: lib/asan/asan_fake_stack.cc
===================================================================
--- lib/asan/asan_fake_stack.cc
+++ lib/asan/asan_fake_stack.cc
@@ -134,6 +134,20 @@
   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;
 
Index: lib/asan/asan_fake_stack.h
===================================================================
--- lib/asan/asan_fake_stack.h
+++ lib/asan/asan_fake_stack.h
@@ -148,6 +148,8 @@
   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.
Index: lib/asan/asan_thread.cc
===================================================================
--- lib/asan/asan_thread.cc
+++ lib/asan/asan_thread.cc
@@ -294,17 +294,21 @@
   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
 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 @@
   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();
 }
Index: lib/lsan/lit_tests/TestCases/use_after_return.cc
===================================================================
--- /dev/null
+++ lib/lsan/lit_tests/TestCases/use_after_return.cc
@@ -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:
Index: lib/lsan/lsan_common.cc
===================================================================
--- lib/lsan/lsan_common.cc
+++ lib/lsan/lsan_common.cc
@@ -151,6 +151,11 @@
   }
 }
 
+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 @@
       }
       ScanRangeForPointers(stack_begin, stack_end, frontier, "STACK",
                            kReachable);
+      ForEachExtraStackRange(os_id, ForEachExtraStackRangeCb, frontier);
     }
 
     if (flags()->use_tls) {
Index: lib/lsan/lsan_common.h
===================================================================
--- lib/lsan/lsan_common.h
+++ lib/lsan/lsan_common.h
@@ -135,6 +135,8 @@
 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
Index: lib/lsan/lsan_thread.cc
===================================================================
--- lib/lsan/lsan_thread.cc
+++ lib/lsan/lsan_thread.cc
@@ -145,6 +145,10 @@
   return true;
 }
 
+void ForEachExtraStackRange(uptr os_id, RangeIteratorCallback callback,
+                            void *arg) {
+}
+
 void LockThreadRegistry() {
   thread_registry->Lock();
 }
Index: lib/sanitizer_common/sanitizer_common.h
===================================================================
--- lib/sanitizer_common/sanitizer_common.h
+++ lib/sanitizer_common/sanitizer_common.h
@@ -446,6 +446,9 @@
 // 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
-------------- next part --------------
A non-text attachment was scrubbed...
Name: D1901.3.patch
Type: text/x-patch
Size: 6662 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/llvm-commits/attachments/20131014/8549fca1/attachment.bin>


More information about the llvm-commits mailing list