[ASan] Add ASan debugging API to get malloc/free stack traces and shadow memory mapping info

Reviewed at http://reviews.llvm.org/D4466

llvm-svn: 213080
This commit is contained in:
Kuba Brecka 2014-07-15 17:33:23 +00:00
parent 8587711164
commit 58f44dce96
6 changed files with 188 additions and 0 deletions

View 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.

View 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

View File

@ -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;
}

View File

@ -90,6 +90,17 @@ extern "C" {
SANITIZER_INTERFACE_ATTRIBUTE
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);

View File

@ -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;
}

View File

@ -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;
}