Hi kcc, glider,

This patch is part of an effort to implement a more generic debugging API, as 
proposed in http://lists.cs.uiuc.edu/pipermail/llvmdev/2014-July/074656.html, 
with first part reviewed at http://reviews.llvm.org/D4466.

Now adding two new APIs:

* __asan_get_report_data which returns a 0 if no asan report happened yet, or 1 
if yes and also returns the PC, BP, SP, address, access type (read/write), 
access size and bug description (e.g. "heap-use-after-free")

* __asan_locate_address which takes a pointer and tries to locate it, i.e. say 
whether it is a heap pointer, a global or a stack, or whether it's a pointer 
into the shadow memory. If global or stack, tries to also return the variable 
name, address and size. If heap, tries to return the chunk address and size.

Generally these should serve as an alternative to "__asan_describe_address", 
which only returns all the data in text form. Having an API to get these data 
could allow having debugging scripts/extensions that could show additional 
information about a variable/expression/pointer. The idea behind having 
__asan_get_report_data is that we could have a special lldb stop reason that 
could show much more information than just "EXC_BAD_ACCESS".

http://reviews.llvm.org/D4527

Files:
  include/sanitizer/asan_interface.h
  lib/asan/asan_debugging.cc
  lib/asan/asan_globals.cc
  lib/asan/asan_interface_internal.h
  lib/asan/asan_report.cc
  lib/asan/asan_report.h
  test/asan/TestCases/debug_locate.cc
  test/asan/TestCases/debug_report.cc
Index: include/sanitizer/asan_interface.h
===================================================================
--- include/sanitizer/asan_interface.h
+++ include/sanitizer/asan_interface.h
@@ -62,6 +62,34 @@
   // 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 error.
+  // If an error has been (or is being) reported, returns the pc, bp, sp,
+  // address, access type, access type and bug description, and the return
+  // value of the function is 1. If no error occurred yet, returns 0.
+  int __asan_get_report_data(void **pc, void **bp, void **sp, void **addr,
+                             int *is_write, size_t *access_size,
+                             const char **bug_description);
+
+  // Address/memory type from ASan's point of view.
+  typedef enum {
+    __ADDRESS_TYPE_UNKNOWN = 0,
+    __ADDRESS_TYPE_SHADOW_LOW = 1,
+    __ADDRESS_TYPE_SHADOW_GAP = 2,
+    __ADDRESS_TYPE_SHADOW_HIGH = 3,
+    __ADDRESS_TYPE_GLOBAL = 4,
+    __ADDRESS_TYPE_STACK = 5,
+    __ADDRESS_TYPE_HEAP = 6,
+    __ADDRESS_TYPE_HEAP_INVALID = 7
+  } __asan_address_type;
+
+  // Useful for calling from the debugger to get information about a pointer.
+  // Return one of the __ADDRESS_TYPE_* enum values. 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'.
+  int __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
@@ -18,9 +18,97 @@
 #include "asan_internal.h"
 #include "asan_mapping.h"
 #include "asan_thread.h"
+#include "asan_report.h"
 
 namespace __asan {
 
+typedef enum {
+  __ADDRESS_TYPE_UNKNOWN = 0,
+  __ADDRESS_TYPE_SHADOW_LOW = 1,
+  __ADDRESS_TYPE_SHADOW_GAP = 2,
+  __ADDRESS_TYPE_SHADOW_HIGH = 3,
+  __ADDRESS_TYPE_GLOBAL = 4,
+  __ADDRESS_TYPE_STACK = 5,
+  __ADDRESS_TYPE_HEAP = 6,
+  __ADDRESS_TYPE_HEAP_INVALID = 7
+} __asan_address_type;
+
+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);
+
+  char *p;
+  uptr n_objects = (uptr)internal_simple_strtoll(frame_descr, &p, 10);
+  for (uptr i = 0; i < n_objects; i++) {
+    uptr beg  = (uptr)internal_simple_strtoll(p, &p, 10);
+    uptr size = (uptr)internal_simple_strtoll(p, &p, 10);
+    uptr len  = (uptr)internal_simple_strtoll(p, &p, 10);
+    if (beg == 0 || size == 0 || *p != ' ') break;
+    p++;
+
+    if (offset <= beg + size) {
+      if (name) {
+        uptr var_name_len = len;
+        if (len > name_size - 1) var_name_len = name_size - 1;
+        memcpy(name, p, var_name_len);
+        name[var_name_len] = '\0';
+      }
+      if (region_address) *region_address = addr - (offset - beg);
+      if (region_size) *region_size = size;
+      break;
+    }
+
+    p += len;
+  }
+}
+
+int AsanLocateAddress(uptr addr, char *name, uptr name_size,
+                      uptr *region_address, uptr *region_size) {
+  // check for shadow
+  if (!AddrIsInMem(addr)) {
+    if (AddrIsInShadowGap(addr)) {
+      return __ADDRESS_TYPE_SHADOW_GAP;
+    }
+    if (AddrIsInHighShadow(addr)) {
+      return __ADDRESS_TYPE_SHADOW_HIGH;
+    }
+    if (AddrIsInLowShadow(addr)) {
+      return __ADDRESS_TYPE_SHADOW_LOW;
+    }
+    return __ADDRESS_TYPE_UNKNOWN;
+  }
+
+  // check for global
+  if (GetInfoForAddressIfGlobal(addr, name, name_size, region_address,
+                                region_size)) {
+    return __ADDRESS_TYPE_GLOBAL;
+  }
+
+  // check for stack
+  asanThreadRegistry().Lock();
+  AsanThread *thread = FindThreadByStackAddress(addr);
+  asanThreadRegistry().Unlock();
+  if (thread) {
+    AsanGetStackVarInfo(addr, name, name_size, region_address, region_size,
+                        thread);
+    return __ADDRESS_TYPE_STACK;
+  }
+
+  // assume it is a heap address
+  AsanChunkView chunk = FindHeapChunkByAddress(addr);
+  if (!chunk.IsValid()) {
+    return __ADDRESS_TYPE_HEAP_INVALID;
+  }
+
+  if (region_address) *region_address = chunk.Beg();
+  if (region_size) *region_size = chunk.UsedSize();
+
+  return __ADDRESS_TYPE_HEAP;
+}
+
 uptr AsanGetStack(uptr addr, uptr *trace, uptr size, u32 *thread_id,
                   bool alloc_stack) {
   AsanChunkView chunk = FindHeapChunkByAddress(addr);
@@ -56,6 +144,12 @@
 using namespace __asan;
 
 SANITIZER_INTERFACE_ATTRIBUTE
+int __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
@@ -90,6 +90,19 @@
   return res;
 }
 
+bool GetInfoForAddressIfGlobal(uptr addr, char *name, uptr name_size,
+                               uptr *region_address, uptr *region_size) {
+  BlockingMutexLock lock(&mu_for_globals);
+  for (ListOfGlobals *l = list_of_all_globals; l; l = l->next) {
+    const Global &g = *l->g;
+    if (GetInfoForAddressRelativeToGlobal(addr, g, name, name_size,
+                                          region_address, region_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
@@ -91,6 +91,15 @@
   void __asan_describe_address(uptr addr);
 
   SANITIZER_INTERFACE_ATTRIBUTE
+  int __asan_get_report_data(uptr *pc, uptr *bp, uptr *sp, uptr *addr,
+                             uptr *is_write, uptr *access_size,
+                             const char **bug_description);
+
+  SANITIZER_INTERFACE_ATTRIBUTE
+  int __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.cc
===================================================================
--- lib/asan/asan_report.cc
+++ lib/asan/asan_report.cc
@@ -31,6 +31,15 @@
 static uptr error_message_buffer_pos = 0;
 static uptr error_message_buffer_size = 0;
 
+static bool report_already_happened = 0;
+static uptr report_pc = 0;
+static uptr report_sp = 0;
+static uptr report_bp = 0;
+static uptr report_addr = 0;
+static bool report_is_write = 0;
+static uptr report_access_size = 0;
+static const char *report_description = 0;
+
 void AppendToErrorMessageBuffer(const char *buffer) {
   if (error_message_buffer) {
     uptr length = internal_strlen(buffer);
@@ -232,9 +241,10 @@
     str->append(":%d", g.location->column_no);
 }
 
+static const uptr kMinimalDistanceFromAnotherGlobal = 64;
+
 bool DescribeAddressRelativeToGlobal(uptr addr, uptr size,
                                      const __asan_global &g) {
-  static const uptr kMinimalDistanceFromAnotherGlobal = 64;
   if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
   if (addr >= g.beg + g.size_with_redzone) return false;
   InternalScopedString str(4096);
@@ -262,6 +272,24 @@
   return true;
 }
 
+bool GetInfoForAddressRelativeToGlobal(uptr addr, const __asan_global &g,
+                                      char *name, uptr name_size,
+                                      uptr *region_address, uptr *region_size) {
+  if (addr <= g.beg - kMinimalDistanceFromAnotherGlobal) return false;
+  if (addr >= g.beg + g.size_with_redzone) return false;
+
+  if (name) {
+    uptr name_len = strlen(g.name);
+    if (name_len > name_size - 1) name_len = name_size - 1;
+    memcpy(name, g.name, name_len);
+    name[name_len] = '\0';
+  }
+  if (region_address) *region_address = g.beg;
+  if (region_size) *region_size = g.size;
+
+  return true;
+}
+
 bool DescribeAddressIfShadow(uptr addr) {
   if (AddrIsInMem(addr))
     return false;
@@ -860,8 +888,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)) {
@@ -908,6 +934,18 @@
         break;
     }
   }
+
+  report_already_happened = 1;
+  report_description = bug_descr;
+  report_pc = pc;
+  report_bp = bp;
+  report_sp = sp;
+  report_addr = addr;
+  report_is_write = is_write;
+  report_access_size = access_size;
+
+  ScopedInErrorReport in_report;
+
   Decorator d;
   Printf("%s", d.Warning());
   Report("ERROR: AddressSanitizer: %s on address "
@@ -946,6 +984,23 @@
   DescribeAddress(addr, 1);
 }
 
+extern "C" SANITIZER_INTERFACE_ATTRIBUTE
+int __asan_get_report_data(uptr *pc, uptr *bp, uptr *sp, uptr *addr,
+                           uptr *is_write, uptr *access_size,
+                           const char **bug_description) {
+  if (!report_already_happened) return 0;
+
+  if (pc) *pc = report_pc;
+  if (bp) *bp = report_bp;
+  if (sp) *sp = report_sp;
+  if (addr) *addr = report_addr;
+  if (is_write) *is_write = report_is_write;
+  if (access_size) *access_size = report_access_size;
+  if (bug_description) *bug_description = report_description;
+
+  return 1;
+}
+
 extern "C" {
 SANITIZER_INTERFACE_ATTRIBUTE
 void __sanitizer_ptr_sub(void *a, void *b) {
Index: lib/asan/asan_report.h
===================================================================
--- lib/asan/asan_report.h
+++ lib/asan/asan_report.h
@@ -24,6 +24,11 @@
 bool DescribeAddressIfGlobal(uptr addr, uptr access_size);
 bool DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
                                      const __asan_global &g);
+bool GetInfoForAddressRelativeToGlobal(uptr addr, const __asan_global &g,
+                                       char *name, uptr name_size,
+                                       uptr *region_address, uptr *region_size);
+bool GetInfoForAddressIfGlobal(uptr addr, char *name, uptr name_size,
+                               uptr *region_address, uptr *region_size);
 bool DescribeAddressIfShadow(uptr addr);
 bool DescribeAddressIfStack(uptr addr, uptr access_size);
 // Determines memory type on its own.
Index: test/asan/TestCases/debug_locate.cc
===================================================================
--- test/asan/TestCases/debug_locate.cc
+++ test/asan/TestCases/debug_locate.cc
@@ -0,0 +1,99 @@
+// 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 <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/asan_interface.h>
+
+int global_var;
+
+int main() {
+  int local_var;
+  char *heap_ptr = (char *)malloc(10);
+
+  char name[100];
+  void *region_address;
+  size_t region_size;
+  int type;
+
+  type = __asan_locate_address(&global_var, name, 100,
+                               &region_address, &region_size);
+
+  printf("global: %s, %s, %s, %s\n",
+         name,
+         (type == __ADDRESS_TYPE_GLOBAL) ? "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,
+                               &region_address, &region_size);
+
+  printf("global+1: %s, %s, %s, %s\n",
+         name,
+         (type == __ADDRESS_TYPE_GLOBAL) ? "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,
+                               &region_address, &region_size);
+
+  printf("local: %s, %s, %s, %s\n",
+         name,
+         (type == __ADDRESS_TYPE_STACK) ? "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,
+                               &region_address, &region_size);
+
+  printf("local+1: %s, %s, %s, %s\n",
+         name,
+         (type == __ADDRESS_TYPE_STACK) ? "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,
+                               &region_address, &region_size);
+
+  printf("heap: %s, %s, %s\n",
+         (type == __ADDRESS_TYPE_HEAP) ? "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,
+                               &region_address, &region_size);
+
+  printf("heap+1: %s, %s, %s\n",
+         (type == __ADDRESS_TYPE_HEAP) ? "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",
+         (type == __ADDRESS_TYPE_SHADOW_HIGH ||
+          type == __ADDRESS_TYPE_SHADOW_LOW) ? "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",
+         (type == __ADDRESS_TYPE_SHADOW_GAP) ? "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,50 @@
+// 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 <stdlib.h>
+#include <stdio.h>
+#include <sanitizer/asan_interface.h>
+
+int main() {
+  char *heap_ptr = (char *)malloc(10);
+  free(heap_ptr);
+
+  void *pc, *bp, *sp, *addr;
+  int is_write;
+  size_t access_size;
+  const char *description;
+
+  int retval = __asan_get_report_data(&pc, &bp, &sp, &addr, &is_write,
+                                      &access_size, &description);
+  fprintf(stderr, "%s\n", (retval == 0) ? "no report" : ""); // CHECK: no report
+
+  heap_ptr[0] = 'A'; // BOOM
+
+  return 0;
+}
+
+void __asan_on_error() {
+  void *pc, *bp, *sp, *addr;
+  int is_write;
+  size_t access_size;
+  const char *description;
+
+  int retval = __asan_get_report_data(&pc, &bp, &sp, &addr, &is_write,
+                                      &access_size, &description);
+
+  fprintf(stderr, "%s\n", (retval == 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
_______________________________________________
lldb-commits mailing list
lldb-commits@cs.uiuc.edu
http://lists.cs.uiuc.edu/mailman/listinfo/lldb-commits

Reply via email to