diff options
author | Kuba Brecka <kuba.brecka@gmail.com> | 2014-07-15 17:33:23 +0000 |
---|---|---|
committer | Kuba Brecka <kuba.brecka@gmail.com> | 2014-07-15 17:33:23 +0000 |
commit | b810c83f4bbb86a25d1a42a843a27ce9b3b17b04 (patch) | |
tree | c084f216d51e52521433ba5cb917d1f1fcba72b6 | |
parent | a1e646a6e39a2c8057069f299d790a89878007fd (diff) | |
download | compiler-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.h | 16 | ||||
-rw-r--r-- | lib/asan/CMakeLists.txt | 1 | ||||
-rw-r--r-- | lib/asan/asan_debugging.cc | 74 | ||||
-rw-r--r-- | lib/asan/asan_interface_internal.h | 11 | ||||
-rw-r--r-- | test/asan/TestCases/debug_mapping.cc | 24 | ||||
-rw-r--r-- | test/asan/TestCases/debug_stacks.cc | 62 |
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; +} |