Index: include/sanitizer/asan_interface.h =================================================================== --- include/sanitizer/asan_interface.h +++ include/sanitizer/asan_interface.h @@ -62,6 +62,22 @@ // 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: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -10,6 +10,7 @@ set(ASAN_SOURCES asan_allocator2.cc asan_activation.cc + asan_debugging.cc asan_fake_stack.cc asan_globals.cc asan_interceptors.cc Index: lib/asan/asan_debugging.cc =================================================================== --- lib/asan/asan_debugging.cc +++ 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; +} Index: lib/asan/asan_interface_internal.h =================================================================== --- lib/asan/asan_interface_internal.h +++ lib/asan/asan_interface_internal.h @@ -85,6 +85,17 @@ 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); Index: test/asan/TestCases/debug_mapping.cc =================================================================== --- test/asan/TestCases/debug_mapping.cc +++ 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 +#include +#include + +// 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; +} Index: test/asan/TestCases/debug_stacks.cc =================================================================== --- test/asan/TestCases/debug_stacks.cc +++ 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 +#include +#include + +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; +}