Index: include/sanitizer/asan_interface.h =================================================================== --- include/sanitizer/asan_interface.h +++ include/sanitizer/asan_interface.h @@ -62,6 +62,24 @@ // 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. Returns 1 on success, 0 on error. + // 'trace' should point to an allocated buffer of size '*frame_count', + // where the real number of frames will be returned as well. + int __asan_get_alloc_stack(void *addr, void **trace, size_t *frame_count, + void **top_frame_bp, int *thread_id); + + // Useful for calling from the debugger to get the free stack trace + // and thread ID for a heap address. Returns 1 on success, 0 on error. + // 'trace' should point to an allocated buffer of size '*frame_count', + // where the real number of frames will be returned as well. + int __asan_get_free_stack(void *addr, void **trace, size_t *frame_count, + void **top_frame_bp, 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" + +using namespace __asan; + +int __asan_get_stack(uptr addr, uptr *trace, uptr *frame_count, + uptr *top_frame_bp, u32 *thread_id, int 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 (top_frame_bp) *top_frame_bp = stack.top_frame_bp; + if (trace && frame_count) { + if (*frame_count > kStackTraceMax) + *frame_count = kStackTraceMax; + if (*frame_count > stack.size) + *frame_count = stack.size; + for (uptr i = 0; i < *frame_count; i++) + trace[i] = StackTrace::GetPreviousInstructionPc(stack.trace[i]); + } + + return 1; +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __asan_get_alloc_stack(uptr addr, uptr *trace, uptr *frame_count, + uptr *top_frame_bp, u32 *thread_id) { + return __asan_get_stack(addr, trace, frame_count, top_frame_bp, thread_id, 1); +} + +SANITIZER_INTERFACE_ATTRIBUTE +int __asan_get_free_stack(uptr addr, uptr *trace, uptr *frame_count, + uptr *top_frame_bp, u32 *thread_id) { + return __asan_get_stack(addr, trace, frame_count, top_frame_bp, thread_id, 0); +} + +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 + int __asan_get_alloc_stack(uptr addr, uptr *trace, uptr *frame_count, + uptr *top_frame_bp, u32 *thread_id); + + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_get_free_stack(uptr addr, uptr *trace, uptr *frame_count, + uptr *top_frame_bp, 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: lib/asan/tests/CMakeLists.txt =================================================================== --- lib/asan/tests/CMakeLists.txt +++ lib/asan/tests/CMakeLists.txt @@ -139,6 +139,7 @@ set(ASAN_NOINST_TEST_SOURCES ${COMPILER_RT_GTEST_SOURCE} + asan_debugging_noinst_test.cc asan_fake_stack_test.cc asan_noinst_test.cc asan_test_main.cc) Index: lib/asan/tests/asan_debugging_noinst_test.cc =================================================================== --- lib/asan/tests/asan_debugging_noinst_test.cc +++ lib/asan/tests/asan_debugging_noinst_test.cc @@ -0,0 +1,27 @@ +//===-- asan_debugging_noinst_test.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. +// +// Tests for the debugging API. +//===----------------------------------------------------------------------===// +#include "asan_test_utils.h" +#include "asan_mapping.h" +#include "asan_internal.h" + +TEST(AddressSanitizerDebuggingInterface, GetShadowMappingTest) { + size_t scale, offset; + __asan_get_shadow_mapping(&scale, &offset); + + EXPECT_EQ(scale, SHADOW_SCALE); + EXPECT_EQ(offset, SHADOW_OFFSET); + + size_t wild_ptr = 0xdeadbabe; + EXPECT_EQ((wild_ptr >> scale) + offset, __asan::MemToShadow(wild_ptr)); +} Index: test/asan/TestCases/debug_stacks.cc =================================================================== --- test/asan/TestCases/debug_stacks.cc +++ test/asan/TestCases/debug_stacks.cc @@ -0,0 +1,55 @@ +// Check that the stack trace debuggin 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; + void *top_frame_bp; + int thread_id; + int retval = __asan_get_alloc_stack(mem, trace, &num_frames, &top_frame_bp, + &thread_id); + + fprintf(stderr, "alloc stack retval = 1\n"); // CHECK: alloc stack retval = 1 + 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; + retval = __asan_get_free_stack(mem, trace, &num_frames, &top_frame_bp, + &thread_id); + + fprintf(stderr, "free stack retval = 1\n"); // CHECK: free stack retval = 1 + 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; +}