[ASan] Add ASan debugging API to get malloc/free stack traces and shadow memory mappi...
authorKuba Brecka <kuba.brecka@gmail.com>
Tue, 15 Jul 2014 17:33:23 +0000 (17:33 +0000)
committerKuba Brecka <kuba.brecka@gmail.com>
Tue, 15 Jul 2014 17:33:23 +0000 (17:33 +0000)
Reviewed at http://reviews.llvm.org/D4466

llvm-svn: 213080

compiler-rt/include/sanitizer/asan_interface.h
compiler-rt/lib/asan/CMakeLists.txt
compiler-rt/lib/asan/asan_debugging.cc [new file with mode: 0644]
compiler-rt/lib/asan/asan_interface_internal.h
compiler-rt/test/asan/TestCases/debug_mapping.cc [new file with mode: 0644]
compiler-rt/test/asan/TestCases/debug_stacks.cc [new file with mode: 0644]

index 23fc178..e8163cf 100644 (file)
@@ -62,6 +62,22 @@ extern "C" {
   // Print the description of addr (useful when debugging in gdb).
   void __asan_describe_address(void *addr);
 
+  // Useful for calling from the debugger to get the allocation stack trace
+  // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
+  // returns the number of stored frames or 0 on error.
+  size_t __asan_get_alloc_stack(void *addr, void **trace, size_t size,
+                                int *thread_id);
+
+  // Useful for calling from the debugger to get the free stack trace
+  // and thread ID for a heap address. Stores up to 'size' frames into 'trace',
+  // returns the number of stored frames or 0 on error.
+  size_t __asan_get_free_stack(void *addr, void **trace, size_t size,
+                               int *thread_id);
+
+  // Useful for calling from the debugger to get the current shadow memory
+  // mapping.
+  void __asan_get_shadow_mapping(size_t *shadow_scale, size_t *shadow_offset);
+
   // This is an internal function that is called to report an error.
   // However it is still a part of the interface because users may want to
   // set a breakpoint on this function in a debugger.
index b23a7a2..acf000d 100644 (file)
@@ -10,6 +10,7 @@ endif()
 set(ASAN_SOURCES
   asan_allocator2.cc
   asan_activation.cc
+  asan_debugging.cc
   asan_fake_stack.cc
   asan_globals.cc
   asan_interceptors.cc
diff --git a/compiler-rt/lib/asan/asan_debugging.cc b/compiler-rt/lib/asan/asan_debugging.cc
new file mode 100644 (file)
index 0000000..05198a7
--- /dev/null
@@ -0,0 +1,74 @@
+//===-- asan_debugging.cc -------------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of AddressSanitizer, an address sanity checker.
+//
+// This file contains various functions that are generally useful to call when
+// using a debugger (LLDB, GDB).
+//===----------------------------------------------------------------------===//
+
+#include "asan_allocator.h"
+#include "asan_flags.h"
+#include "asan_internal.h"
+#include "asan_mapping.h"
+#include "asan_thread.h"
+
+namespace __asan {
+
+uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
+                  bool alloc_stack) {
+  AsanChunkView chunk = FindHeapChunkByAddress(addr);
+  if (!chunk.IsValid()) return 0;
+
+  StackTrace stack;
+  if (alloc_stack) {
+    if (chunk.AllocTid() == kInvalidTid) return 0;
+    chunk.GetAllocStack(&stack);
+    if (thread_id) *thread_id = chunk.AllocTid();
+  } else {
+    if (chunk.FreeTid() == kInvalidTid) return 0;
+    chunk.GetFreeStack(&stack);
+    if (thread_id) *thread_id = chunk.FreeTid();
+  }
+
+  if (trace && size) {
+    if (size > kStackTraceMax)
+      size = kStackTraceMax;
+    if (size > stack.size)
+      size = stack.size;
+    for (uptr i = 0; i < size; i++)
+      trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]);
+
+    return size;
+  }
+
+  return 0;
+}
+
+}  // namespace __asan
+
+using namespace __asan;
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+  return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ true);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id) {
+  return AsanGetStack(addr, trace, size, thread_id, /* alloc_stack */ false);
+}
+
+SANITIZER_INTERFACE_ATTRIBUTE
+void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset) {
+  if (shadow_scale)
+    *shadow_scale = SHADOW_SCALE;
+  if (shadow_offset)
+    *shadow_offset = SHADOW_OFFSET;
+}
index 4d3d8bb..7985236 100644 (file)
@@ -91,6 +91,17 @@ extern "C" {
   void __asan_describe_address(uptr addr);
 
   SANITIZER_INTERFACE_ATTRIBUTE
+  uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size,
+                              u32 *thread_id);
+
+  SANITIZER_INTERFACE_ATTRIBUTE
+  uptr __asan_get_free_stack(uptr addr, uptr *trace, uptr size,
+                             u32 *thread_id);
+
+  SANITIZER_INTERFACE_ATTRIBUTE
+  void __asan_get_shadow_mapping(uptr *shadow_scale, uptr *shadow_offset);
+
+  SANITIZER_INTERFACE_ATTRIBUTE
   void __asan_report_error(uptr pc, uptr bp, uptr sp,
                            uptr addr, int is_write, uptr access_size);
 
diff --git a/compiler-rt/test/asan/TestCases/debug_mapping.cc b/compiler-rt/test/asan/TestCases/debug_mapping.cc
new file mode 100644 (file)
index 0000000..f96abf6
--- /dev/null
@@ -0,0 +1,24 @@
+// Checks that the debugging API returns correct shadow scale and offset.
+// RUN: %clangxx_asan -O %s -o %t
+// RUN: env ASAN_OPTIONS=verbosity=1 %run %t 2>&1 | FileCheck %s
+
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+// printed because of verbosity=1
+// CHECK: SHADOW_SCALE: [[SCALE:[0-9]+]]
+// CHECK: SHADOW_OFFSET: [[OFFSET:[0-9]+]]
+
+int main() {
+  size_t scale, offset;
+  __asan_get_shadow_mapping(&scale, &offset);
+
+  fprintf(stderr, "scale: %lx\n", scale);
+  fprintf(stderr, "offset: %lx\n", offset);
+
+  // CHECK: scale: [[SCALE]]
+  // CHECK: offset: [[OFFSET]]
+
+  return 0;
+}
diff --git a/compiler-rt/test/asan/TestCases/debug_stacks.cc b/compiler-rt/test/asan/TestCases/debug_stacks.cc
new file mode 100644 (file)
index 0000000..57bb546
--- /dev/null
@@ -0,0 +1,62 @@
+// Check that the stack trace debugging API works and returns correct
+// malloc and free stacks.
+// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s
+
+#include <sanitizer/asan_interface.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+char *mem;
+void func1() {
+  mem = (char *)malloc(10);
+}
+
+void func2() {
+  free(mem);
+}
+
+int main() {
+  func1();
+  func2();
+
+  void *trace[100];
+  size_t num_frames = 100;
+  int thread_id;
+  num_frames = __asan_get_alloc_stack(mem, trace, num_frames, &thread_id);
+
+  fprintf(stderr, "alloc stack retval %s\n", (num_frames > 0 && num_frames < 10)
+          ? "ok" : "");
+  // CHECK: alloc stack retval ok
+  fprintf(stderr, "thread id = %d\n", thread_id);
+  // CHECK: thread id = 0
+  fprintf(stderr, "0x%lx\n", trace[0]);
+  // CHECK: [[ALLOC_FRAME_0:0x[0-9a-f]+]]
+  fprintf(stderr, "0x%lx\n", trace[1]);
+  // CHECK: [[ALLOC_FRAME_1:0x[0-9a-f]+]]
+
+  num_frames = 100;
+  num_frames = __asan_get_free_stack(mem, trace, num_frames, &thread_id);
+
+  fprintf(stderr, "free stack retval %s\n", (num_frames > 0 && num_frames < 10)
+          ? "ok" : "");
+  // CHECK: free stack retval ok
+  fprintf(stderr, "thread id = %d\n", thread_id);
+  // CHECK: thread id = 0
+  fprintf(stderr, "0x%lx\n", trace[0]);
+  // CHECK: [[FREE_FRAME_0:0x[0-9a-f]+]]
+  fprintf(stderr, "0x%lx\n", trace[1]);
+  // CHECK: [[FREE_FRAME_1:0x[0-9a-f]+]]
+
+  mem[0] = 'A'; // BOOM
+
+  // CHECK: ERROR: AddressSanitizer: heap-use-after-free
+  // CHECK: WRITE of size 1 at 0x{{.*}}
+  // CHECK: freed by thread T0 here:
+  // CHECK: #0 [[FREE_FRAME_0]]
+  // CHECK: #1 [[FREE_FRAME_1]]
+  // CHECK: previously allocated by thread T0 here:
+  // CHECK: #0 [[ALLOC_FRAME_0]]
+  // CHECK: #1 [[ALLOC_FRAME_1]]
+
+  return 0;
+}