aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKuba Brecka <kuba.brecka@gmail.com>2014-07-15 17:33:23 +0000
committerKuba Brecka <kuba.brecka@gmail.com>2014-07-15 17:33:23 +0000
commitb810c83f4bbb86a25d1a42a843a27ce9b3b17b04 (patch)
treec084f216d51e52521433ba5cb917d1f1fcba72b6
parenta1e646a6e39a2c8057069f299d790a89878007fd (diff)
downloadcompiler-rt-b810c83f4bbb86a25d1a42a843a27ce9b3b17b04.tar.gz
[ASan] Add ASan debugging API to get malloc/free stack traces and shadow memory mapping info
Reviewed at http://reviews.llvm.org/D4466 git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@213080 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--include/sanitizer/asan_interface.h16
-rw-r--r--lib/asan/CMakeLists.txt1
-rw-r--r--lib/asan/asan_debugging.cc74
-rw-r--r--lib/asan/asan_interface_internal.h11
-rw-r--r--test/asan/TestCases/debug_mapping.cc24
-rw-r--r--test/asan/TestCases/debug_stacks.cc62
6 files changed, 188 insertions, 0 deletions
diff --git a/include/sanitizer/asan_interface.h b/include/sanitizer/asan_interface.h
index 23fc1789f..e8163cfc3 100644
--- a/include/sanitizer/asan_interface.h
+++ b/include/sanitizer/asan_interface.h
@@ -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.
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index b23a7a2e5..acf000d39 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -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/lib/asan/asan_debugging.cc b/lib/asan/asan_debugging.cc
new file mode 100644
index 000000000..05198a787
--- /dev/null
+++ b/lib/asan/asan_debugging.cc
@@ -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;
+}
diff --git a/lib/asan/asan_interface_internal.h b/lib/asan/asan_interface_internal.h
index 4d3d8bb4d..7985236d6 100644
--- a/lib/asan/asan_interface_internal.h
+++ b/lib/asan/asan_interface_internal.h
@@ -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/test/asan/TestCases/debug_mapping.cc b/test/asan/TestCases/debug_mapping.cc
new file mode 100644
index 000000000..f96abf6d1
--- /dev/null
+++ b/test/asan/TestCases/debug_mapping.cc
@@ -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/test/asan/TestCases/debug_stacks.cc b/test/asan/TestCases/debug_stacks.cc
new file mode 100644
index 000000000..57bb54650
--- /dev/null
+++ b/test/asan/TestCases/debug_stacks.cc
@@ -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;
+}