Index: include/sanitizer/asan_interface.h =================================================================== --- include/sanitizer/asan_interface.h +++ include/sanitizer/asan_interface.h @@ -62,6 +62,32 @@ // Print the description of addr (useful when debugging in gdb). void __asan_describe_address(void *addr); + // Useful for calling from a debugger to get information about an ASan error. + // Returns 1 if an error has been (or is being) reported, otherwise returns 0. + int __asan_report_present(); + + // Useful for calling from a debugger to get information about an ASan error. + // If an error has been (or is being) reported, the following functions return + // the pc, bp, sp, address, access type (0 = read, 1 = write), access size and + // bug description (e.g. "heap-use-after-free"). Otherwise they return 0. + void *__asan_get_report_pc(); + void *__asan_get_report_bp(); + void *__asan_get_report_sp(); + void *__asan_get_report_address(); + int __asan_get_report_access_type(); + size_t __asan_get_report_access_size(); + const char *__asan_get_report_description(); + + // Useful for calling from the debugger to get information about a pointer. + // Returns the category of the given pointer as a constant string. + // Possible return values are "global", "stack", "stack-fake", "heap", + // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown". + // If global or stack, tries to also return the variable name, address and + // size. If heap, tries to return the chunk address and size. 'name' should + // point to an allocated buffer of size 'name_size'. + const char *__asan_locate_address(void *addr, char *name, size_t name_size, + void **region_address, size_t *region_size); + // 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. Index: lib/asan/asan_debugging.cc =================================================================== --- lib/asan/asan_debugging.cc +++ lib/asan/asan_debugging.cc @@ -17,10 +17,70 @@ #include "asan_flags.h" #include "asan_internal.h" #include "asan_mapping.h" +#include "asan_report.h" #include "asan_thread.h" namespace __asan { +void AsanGetStackVarInfo(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size, + AsanThread *t) { + uptr offset = 0; + uptr frame_pc = 0; + const char *frame_descr = t->GetFrameNameByAddr(addr, &offset, &frame_pc); + InternalMmapVector vars(16); + ParseFrameDescription(frame_descr, &vars); + + for (uptr i = 0; i < vars.size(); i++) { + if (offset <= vars[i].beg + vars[i].size) { + if (name) { + uptr var_name_len = vars[i].name_len; + if (vars[i].name_len > name_size - 1) var_name_len = name_size - 1; + memcpy(name, vars[i].name_pos, var_name_len); + name[var_name_len] = '\0'; + } + if (region_address) *region_address = addr - (offset - vars[i].beg); + if (region_size) *region_size = vars[i].size; + break; + } + } +} + +const char *AsanLocateAddress(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size) { + // Check if addr is in shadow memory. + const char *stack_address_type = 0; + if (DescribeAddressIfShadow(addr, &stack_address_type, /* print */ false)) { + return stack_address_type; + } + + // Check if addr is in global memory. + if (GetInfoForAddressIfGlobal(addr, name, name_size, region_address, + region_size)) { + return "global"; + } + + // Check if addr is a pointer into a stack. + asanThreadRegistry().Lock(); + AsanThread *thread = FindThreadByStackAddress(addr); + asanThreadRegistry().Unlock(); + if (thread) { + AsanGetStackVarInfo(addr, name, name_size, region_address, region_size, + thread); + return "stack"; + } + + // Let's assume addr is a heap address. + AsanChunkView chunk = FindHeapChunkByAddress(addr); + if (!chunk.IsValid()) + return "heap-invalid"; + + if (region_address) *region_address = chunk.Beg(); + if (region_size) *region_size = chunk.UsedSize(); + + return "heap"; +} + uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id, bool alloc_stack) { AsanChunkView chunk = FindHeapChunkByAddress(addr); @@ -56,6 +116,12 @@ using namespace __asan; SANITIZER_INTERFACE_ATTRIBUTE +const char *__asan_locate_address(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size) { + return AsanLocateAddress(addr, name, name_size, region_address, region_size); +} + +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); } Index: lib/asan/asan_globals.cc =================================================================== --- lib/asan/asan_globals.cc +++ lib/asan/asan_globals.cc @@ -82,19 +82,44 @@ } } -bool DescribeAddressIfGlobal(uptr addr, uptr size) { +bool DescribeOrGetInfoIfGlobal(uptr addr, uptr size, bool print, + Global *output_global) { if (!flags()->report_globals) return false; BlockingMutexLock lock(&mu_for_globals); bool res = false; for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) { const Global &g = *l->g; - if (flags()->report_globals >= 2) - ReportGlobal(g, "Search"); - res |= DescribeAddressRelativeToGlobal(addr, size, g); + if (print) { + if (flags()->report_globals >= 2) + ReportGlobal(g, "Search"); + res |= DescribeAddressRelativeToGlobal(addr, size, g); + } else { + if (IsAddressNearGlobal(addr, g)) { + if (output_global) *output_global = g; + return true; + } + } } return res; } +bool DescribeAddressIfGlobal(uptr addr, uptr size) { + return DescribeOrGetInfoIfGlobal(addr, size, /* print */ true, + /* output_global */ nullptr); +} + +bool GetInfoForAddressIfGlobal(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size) { + Global g = {}; + if (DescribeOrGetInfoIfGlobal(addr, /* size */ 1, /* print */ false, &g)) { + if (name) internal_strncpy(name, g.name, name_size); + if (region_address) *region_address = g.beg; + if (region_size) *region_size = g.size; + return true; + } + return false; +} + u32 FindRegistrationSite(const Global *g) { CHECK(global_registration_site_vector); for (uptr i = 0, n = global_registration_site_vector->size(); i < n; i++) { Index: lib/asan/asan_interface_internal.h =================================================================== --- lib/asan/asan_interface_internal.h +++ lib/asan/asan_interface_internal.h @@ -90,6 +90,41 @@ SANITIZER_INTERFACE_ATTRIBUTE void __asan_describe_address(uptr addr); + // Returns 1 if an error has been (or is being) reported, otherwise returns 0. + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_report_present(); + + // The following functions extract various information from a report (if a + // report has already been printed out or is currently being generated). + // For PC, BP, SP and memory address returns a pointer, for access type + // return 0 = read or 1 = write, for access size returns a number of bytes, + // and a string description of the report type (e.g. "heap-use-after-free"). + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_report_pc(); + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_report_bp(); + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_report_sp(); + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_report_address(); + SANITIZER_INTERFACE_ATTRIBUTE + int __asan_get_report_access_type(); + SANITIZER_INTERFACE_ATTRIBUTE + uptr __asan_get_report_access_size(); + SANITIZER_INTERFACE_ATTRIBUTE + const char * __asan_get_report_description(); + + // Useful for calling from the debugger to get information about a pointer. + // Returns the category of the given pointer as a constant string. + // Possible return values are "global", "stack", "stack-fake", "heap", + // "heap-invalid", "shadow-low", "shadow-gap", "shadow-high", "unknown". + // If global or stack, tries to also return the variable name, address and + // size. If heap, tries to return the chunk address and size. 'name' should + // point to an allocated buffer of size 'name_size'. + SANITIZER_INTERFACE_ATTRIBUTE + const char * __asan_locate_address(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size); + SANITIZER_INTERFACE_ATTRIBUTE uptr __asan_get_alloc_stack(uptr addr, uptr *trace, uptr size, u32 *thread_id); Index: lib/asan/asan_report.h =================================================================== --- lib/asan/asan_report.h +++ lib/asan/asan_report.h @@ -31,7 +31,11 @@ bool DescribeAddressIfGlobal(uptr addr, uptr access_size); bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size, const __asan_global &g); -bool DescribeAddressIfShadow(uptr addr); +bool IsAddressNearGlobal(uptr addr, const __asan_global &g); +bool GetInfoForAddressIfGlobal(uptr addr, char *name, uptr name_size, + uptr *region_address, uptr *region_size); +bool DescribeAddressIfShadow(uptr addr, const char **shadow_type = 0, + bool print = true); bool ParseFrameDescription(const char *frame_descr, InternalMmapVector *vars); bool DescribeAddressIfStack(uptr addr, uptr access_size); Index: lib/asan/asan_report.cc =================================================================== --- lib/asan/asan_report.cc +++ lib/asan/asan_report.cc @@ -31,6 +31,19 @@ static uptr error_message_buffer_pos = 0; static uptr error_message_buffer_size = 0; +struct ReportData { + uptr pc; + uptr sp; + uptr bp; + uptr addr; + bool is_write; + uptr access_size; + const char *description; +}; + +static bool report_happened = false; +static ReportData report_data = {}; + void AppendToErrorMessageBuffer(const char *buffer) { if (error_message_buffer) { uptr length = internal_strlen(buffer); @@ -235,11 +248,17 @@ str->append(":%d", g.location->column_no); } -bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, - const __asan_global &g) { - static const uptr kMinimalDistanceFromAnotherGlobal = 64; +const uptr kMinimalDistanceFromAnotherGlobal = 64; + +bool IsAddressNearGlobal(uptr addr, const __asan_global &g) { if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false; if (addr >= g.beg + g.size_with_redzone) return false; + return true; +} + +bool DescribeAddressRelativeToGlobal(uptr addr, uptr size, + const __asan_global &g) { + if (!IsAddressNearGlobal(addr, g)) return false; InternalScopedString str(4096); Decorator d; str.append("%s", d.Location()); @@ -265,21 +284,18 @@ return true; } -bool DescribeAddressIfShadow(uptr addr) { +bool DescribeAddressIfShadow(uptr addr, const char **output_type, bool print) { if (AddrIsInMem(addr)) return false; static const char kAddrInShadowReport[] = - "Address %p is located in the %s.\n"; - if (AddrIsInShadowGap(addr)) { - Printf(kAddrInShadowReport, addr, "shadow gap area"); - return true; - } - if (AddrIsInHighShadow(addr)) { - Printf(kAddrInShadowReport, addr, "high shadow area"); - return true; - } - if (AddrIsInLowShadow(addr)) { - Printf(kAddrInShadowReport, addr, "low shadow area"); + "Address %p is located in the %s area.\n"; + const char *area_type = nullptr; + if (AddrIsInShadowGap(addr)) area_type = "shadow gap"; + if (AddrIsInHighShadow(addr)) area_type = "high shadow"; + if (AddrIsInLowShadow(addr)) area_type = "low shadow"; + if (area_type != nullptr) { + if (print) Printf(kAddrInShadowReport, addr, area_type); + if (output_type) *output_type = area_type; return true; } CHECK(0 && "Address is not in memory and not in shadow?"); @@ -893,8 +909,6 @@ void __asan_report_error(uptr pc, uptr bp, uptr sp, uptr addr, int is_write, uptr access_size) { - ScopedInErrorReport in_report; - // Determine the error type. const char *bug_descr = "unknown-crash"; if (AddrIsInMem(addr)) { @@ -942,6 +956,18 @@ break; } } + + report_happened = true; + report_data.description = bug_descr; + report_data.pc = pc; + report_data.bp = bp; + report_data.sp = sp; + report_data.addr = addr; + report_data.is_write = is_write; + report_data.access_size = access_size; + + ScopedInErrorReport in_report; + Decorator d; Printf("%s", d.Warning()); Report("ERROR: AddressSanitizer: %s on address " @@ -983,6 +1009,38 @@ asanThreadRegistry().Unlock(); } +int __asan_report_present() { + return report_happened ? 1 : 0; +} + +uptr __asan_get_report_pc() { + return report_data.pc; +} + +uptr __asan_get_report_bp() { + return report_data.bp; +} + +uptr __asan_get_report_sp() { + return report_data.sp; +} + +uptr __asan_get_report_address() { + return report_data.addr; +} + +int __asan_get_report_access_type() { + return report_data.is_write ? 1 : 0; +} + +uptr __asan_get_report_access_size() { + return report_data.access_size; +} + +const char *__asan_get_report_description() { + return report_data.description; +} + extern "C" { SANITIZER_INTERFACE_ATTRIBUTE void __sanitizer_ptr_sub(void *a, void *b) { Index: test/asan/TestCases/debug_locate.cc =================================================================== --- test/asan/TestCases/debug_locate.cc +++ test/asan/TestCases/debug_locate.cc @@ -0,0 +1,100 @@ +// Checks the ASan memory address type debugging API, makes sure it returns +// the correct memory type for heap, stack, global and shadow addresses and +// that it correctly finds out which region (and name and size) the address +// belongs to. +// RUN: %clangxx_asan -O0 %s -o %t && %run %t 2>&1 | FileCheck %s + +#include +#include +#include +#include + +int global_var; + +int main() { + int local_var; + char *heap_ptr = (char *)malloc(10); + + char name[100]; + void *region_address; + size_t region_size; + const char *type; + + type = __asan_locate_address(&global_var, name, 100, + ®ion_address, ®ion_size); + + printf("global: %s, %s, %s, %s\n", + name, + (strcmp(type, "global") == 0) ? "ok" : "ko", + (region_address == &global_var) ? "ok" : "ko", + (region_size == sizeof(global_var)) ? "ok" : "ko"); + // CHECK: global: global_var, ok, ok, ok + + type = __asan_locate_address((char *)(&global_var)+1, name, 100, + ®ion_address, ®ion_size); + + printf("global+1: %s, %s, %s, %s\n", + name, + (strcmp(type, "global") == 0) ? "ok" : "ko", + (region_address == &global_var) ? "ok" : "ko", + (region_size == sizeof(global_var)) ? "ok" : "ko"); + // CHECK: global+1: global_var, ok, ok, ok + + type = __asan_locate_address(&local_var, name, 100, + ®ion_address, ®ion_size); + + printf("local: %s, %s, %s, %s\n", + name, + (strcmp(type, "stack") == 0) ? "ok" : "ko", + (region_address == &local_var) ? "ok" : "ko", + (region_size == sizeof(local_var)) ? "ok" : "ko"); + // CHECK: local: local_var, ok, ok, ok + + type = __asan_locate_address((char *)(&local_var)+1, name, 100, + ®ion_address, ®ion_size); + + printf("local+1: %s, %s, %s, %s\n", + name, + (strcmp(type, "stack") == 0) ? "ok" : "ko", + (region_address == &local_var) ? "ok" : "ko", + (region_size == sizeof(local_var)) ? "ok" : "ko"); + // CHECK: local+1: local_var, ok, ok, ok + + type = __asan_locate_address(heap_ptr, name, 100, + ®ion_address, ®ion_size); + + printf("heap: %s, %s, %s\n", + (strcmp(type, "heap") == 0) ? "ok" : "ko", + (region_address == heap_ptr) ? "ok" : "ko", + (region_size == 10) ? "ok" : "ko"); + // CHECK: heap: ok, ok, ok + + type = __asan_locate_address(heap_ptr+1, name, 100, + ®ion_address, ®ion_size); + + printf("heap+1: %s, %s, %s\n", + (strcmp(type, "heap") == 0) ? "ok" : "ko", + (region_address == heap_ptr) ? "ok" : "ko", + (region_size == 10) ? "ok" : "ko"); + // CHECK: heap+1: ok, ok, ok + + size_t shadow_scale; + size_t shadow_offset; + __asan_get_shadow_mapping(&shadow_scale, &shadow_offset); + + intptr_t shadow_ptr = (((intptr_t)heap_ptr) >> shadow_scale) + shadow_offset; + + type = __asan_locate_address((void *)shadow_ptr, NULL, 0, NULL, NULL); + printf("shadow: %s\n", + (strcmp(type, "high shadow") == 0 || + strcmp(type, "low shadow") == 0) ? "ok" : "ko"); + // CHECK: shadow: ok + + intptr_t shadow_gap = (shadow_ptr >> shadow_scale) + shadow_offset; + type = __asan_locate_address((void *)shadow_gap, NULL, 0, NULL, NULL); + printf("shadow gap: %s\n", + (strcmp(type, "shadow gap") == 0) ? "ok" : "ko"); + // CHECK: shadow gap: ok + + return 0; +} Index: test/asan/TestCases/debug_report.cc =================================================================== --- test/asan/TestCases/debug_report.cc +++ test/asan/TestCases/debug_report.cc @@ -0,0 +1,48 @@ +// Checks that the ASan debugging API for getting report information +// returns correct values. +// RUN: %clangxx_asan -O0 %s -o %t && not %run %t 2>&1 | FileCheck %s + +#include +#include +#include + +int main() { + char *heap_ptr = (char *)malloc(10); + free(heap_ptr); + int present = __asan_report_present(); + fprintf(stderr, "%s\n", (present == 0) ? "no report" : ""); + // CHECK: no report + heap_ptr[0] = 'A'; // BOOM + return 0; +} + +void __asan_on_error() { + int present = __asan_report_present(); + void *pc = __asan_get_report_pc(); + void *bp = __asan_get_report_bp(); + void *sp = __asan_get_report_sp(); + void *addr = __asan_get_report_address(); + int is_write = __asan_get_report_access_type(); + size_t access_size = __asan_get_report_access_size(); + const char *description = __asan_get_report_description(); + + fprintf(stderr, "%s\n", (present == 1) ? "report" : ""); + // CHECK: report + fprintf(stderr, "pc: %p\n", pc); + // CHECK: pc: 0x[[PC:[0-9a-f]+]] + fprintf(stderr, "bp: %p\n", bp); + // CHECK: bp: 0x[[BP:[0-9a-f]+]] + fprintf(stderr, "sp: %p\n", sp); + // CHECK: sp: 0x[[SP:[0-9a-f]+]] + fprintf(stderr, "addr: %p\n", addr); + // CHECK: addr: 0x[[ADDR:[0-9a-f]+]] + fprintf(stderr, "type: %s\n", (is_write ? "write" : "read")); + // CHECK: type: write + fprintf(stderr, "access_size: %ld\n", access_size); + // CHECK: access_size: 1 + fprintf(stderr, "description: %s\n", description); + // CHECK: description: heap-use-after-free +} + +// CHECK: AddressSanitizer: heap-use-after-free on address {{0x0*}}[[ADDR]] at pc {{0x0*}}[[PC]] bp {{0x0*}}[[BP]] sp {{0x0*}}[[SP]] +// CHECK: WRITE of size 1 at {{0x0*}}[[ADDR]] thread T0