Merge remote-tracking branch 'origin/swift-3.1-branch' into stable

* origin/swift-3.1-branch: (44 commits)
  [asan] Reify ErrorGeneric
  [asan] Remove "Stack partial redzone" from report
  [asan] Store full AddressDescription objects in ErrorInvalidPointerPair
  [asan] Fix GlobalAddressDescription::Print()
  [asan] Reify ErrorInvalidPointerPair
  [asan] Reify ErrorODRViolation
  [asan] Reify ErrorBadParamsToAnnotateContiguousContainer
  [asan] Reify ErrorStringFunctionSizeOverflow
  [asan] Reify ErrorStringFunctionMemoryRangesOverlap
  [asan] Reify ErrorSanitizerGetAllocatedSizeNotOwned
  [asan] Reify ErrorMallocUsableSizeNotOwned
  [asan] Reify ErrorAllocTypeMismatch
  [asan] Reify ErrorFreeNotMalloced
  [asan] Ease dealing with tagged enum ErrorDescription with some macros.
  [asan] Cleanup: Move tid into ErrorBase, add const to BufferedStackTrace, be consistent in constructor arguments and member order.
  [asan] Store access_size in the {Stack,Global}AddressDescription objects
  [asan] Add a new AddressDescription structure, which can describe any type of address.
  [asan] Since r280945 fixed the OS X abort() problem, merge scariness_score_test.cc and make it a general test.
  [asan] Handle SIGBART signal description. (found by buildbot)
  [asan] Reify ErrorDeadlySignal
  ...
diff --git a/lib/asan/CMakeLists.txt b/lib/asan/CMakeLists.txt
index be274df..e37705e 100644
--- a/lib/asan/CMakeLists.txt
+++ b/lib/asan/CMakeLists.txt
@@ -5,6 +5,7 @@
   asan_activation.cc
   asan_debugging.cc
   asan_descriptions.cc
+  asan_errors.cc
   asan_fake_stack.cc
   asan_flags.cc
   asan_globals.cc
diff --git a/lib/asan/asan_allocator.cc b/lib/asan/asan_allocator.cc
index 6a5d227..ebd67b6 100644
--- a/lib/asan/asan_allocator.cc
+++ b/lib/asan/asan_allocator.cc
@@ -530,7 +530,7 @@
 
     if (delete_size && flags()->new_delete_type_mismatch &&
         delete_size != m->UsedSize()) {
-      ReportNewDeleteSizeMismatch(p, m->UsedSize(), delete_size, stack);
+      ReportNewDeleteSizeMismatch(p, delete_size, stack);
     }
 
     QuarantineChunk(m, ptr, stack, alloc_type);
@@ -673,6 +673,9 @@
 uptr AsanChunkView::UsedSize() { return chunk_->UsedSize(); }
 uptr AsanChunkView::AllocTid() { return chunk_->alloc_tid; }
 uptr AsanChunkView::FreeTid() { return chunk_->free_tid; }
+AllocType AsanChunkView::AllocType() {
+  return (enum AllocType)chunk_->alloc_type;
+}
 
 static StackTrace GetStackTraceFromId(u32 id) {
   CHECK(id);
diff --git a/lib/asan/asan_allocator.h b/lib/asan/asan_allocator.h
index 2f9f7aa..55bbbb5 100644
--- a/lib/asan/asan_allocator.h
+++ b/lib/asan/asan_allocator.h
@@ -62,6 +62,7 @@
   u32 GetFreeStackId();
   StackTrace GetAllocStack();
   StackTrace GetFreeStack();
+  AllocType AllocType();
   bool AddrIsInside(uptr addr, uptr access_size, sptr *offset) {
     if (addr >= Beg() && (addr + access_size) <= End()) {
       *offset = addr - Beg();
diff --git a/lib/asan/asan_debugging.cc b/lib/asan/asan_debugging.cc
index b7481ff..37c5922 100644
--- a/lib/asan/asan_debugging.cc
+++ b/lib/asan/asan_debugging.cc
@@ -21,70 +21,32 @@
 #include "asan_report.h"
 #include "asan_thread.h"
 
-namespace __asan {
+namespace {
+using namespace __asan;
 
-void GetInfoForStackVar(uptr addr, AddressDescription *descr, AsanThread *t) {
-  descr->name[0] = 0;
-  descr->region_address = 0;
-  descr->region_size = 0;
-  descr->region_kind = "stack";
-
-  AsanThread::StackFrameAccess access;
-  if (!t->GetStackFrameAccessByAddr(addr, &access))
-    return;
+static void FindInfoForStackVar(uptr addr, const char *frame_descr, uptr offset,
+                                char *name, uptr name_size,
+                                uptr &region_address, uptr &region_size) {
   InternalMmapVector<StackVarDescr> vars(16);
-  if (!ParseFrameDescription(access.frame_descr, &vars)) {
+  if (!ParseFrameDescription(frame_descr, &vars)) {
     return;
   }
 
   for (uptr i = 0; i < vars.size(); i++) {
-    if (access.offset <= vars[i].beg + vars[i].size) {
-      internal_strncat(descr->name, vars[i].name_pos,
-                       Min(descr->name_size, vars[i].name_len));
-      descr->region_address = addr - (access.offset - vars[i].beg);
-      descr->region_size = vars[i].size;
+    if (offset <= vars[i].beg + vars[i].size) {
+      // We use name_len + 1 because strlcpy will guarantee a \0 at the end, so
+      // if we're limiting the copy due to name_len, we add 1 to ensure we copy
+      // the whole name and then terminate with '\0'.
+      internal_strlcpy(name, vars[i].name_pos,
+                       Min(name_size, vars[i].name_len + 1));
+      region_address = addr - (offset - vars[i].beg);
+      region_size = vars[i].size;
       return;
     }
   }
 }
 
-void GetInfoForHeapAddress(uptr addr, AddressDescription *descr) {
-  AsanChunkView chunk = FindHeapChunkByAddress(addr);
-
-  descr->name[0] = 0;
-  descr->region_address = 0;
-  descr->region_size = 0;
-
-  if (!chunk.IsValid()) {
-    descr->region_kind = "heap-invalid";
-    return;
-  }
-
-  descr->region_address = chunk.Beg();
-  descr->region_size = chunk.UsedSize();
-  descr->region_kind = "heap";
-}
-
-void AsanLocateAddress(uptr addr, AddressDescription *descr) {
-  ShadowAddressDescription shadow_descr;
-  if (GetShadowAddressInformation(addr, &shadow_descr)) {
-    descr->region_kind = ShadowNames[shadow_descr.kind];
-    return;
-  }
-  if (GetInfoForAddressIfGlobal(addr, descr)) {
-    return;
-  }
-  asanThreadRegistry().Lock();
-  AsanThread *thread = FindThreadByStackAddress(addr);
-  asanThreadRegistry().Unlock();
-  if (thread) {
-    GetInfoForStackVar(addr, descr, thread);
-    return;
-  }
-  GetInfoForHeapAddress(addr, descr);
-}
-
-static uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
+uptr AsanGetStack(uptr addr, uptr *trace, u32 size, u32 *thread_id,
                          bool alloc_stack) {
   AsanChunkView chunk = FindHeapChunkByAddress(addr);
   if (!chunk.IsValid()) return 0;
@@ -111,18 +73,58 @@
   return 0;
 }
 
-} // namespace __asan
-
-using namespace __asan;
+}  // namespace
 
 SANITIZER_INTERFACE_ATTRIBUTE
 const char *__asan_locate_address(uptr addr, char *name, uptr name_size,
-                                  uptr *region_address, uptr *region_size) {
-  AddressDescription descr = { name, name_size, 0, 0, nullptr };
-  AsanLocateAddress(addr, &descr);
-  if (region_address) *region_address = descr.region_address;
-  if (region_size) *region_size = descr.region_size;
-  return descr.region_kind;
+                                  uptr *region_address_ptr,
+                                  uptr *region_size_ptr) {
+  AddressDescription descr(addr);
+  uptr region_address = 0;
+  uptr region_size = 0;
+  const char *region_kind = nullptr;
+  if (name && name_size > 0) name[0] = 0;
+
+  if (auto shadow = descr.AsShadow()) {
+    // region_{address,size} are already 0
+    switch (shadow->kind) {
+      case kShadowKindLow:
+        region_kind = "low shadow";
+        break;
+      case kShadowKindGap:
+        region_kind = "shadow gap";
+        break;
+      case kShadowKindHigh:
+        region_kind = "high shadow";
+        break;
+    }
+  } else if (auto heap = descr.AsHeap()) {
+    region_kind = "heap";
+    region_address = heap->chunk_access.chunk_begin;
+    region_size = heap->chunk_access.chunk_size;
+  } else if (auto stack = descr.AsStack()) {
+    region_kind = "stack";
+    if (!stack->frame_descr) {
+      // region_{address,size} are already 0
+    } else {
+      FindInfoForStackVar(addr, stack->frame_descr, stack->offset, name,
+                          name_size, region_address, region_size);
+    }
+  } else if (auto global = descr.AsGlobal()) {
+    region_kind = "global";
+    auto &g = global->globals[0];
+    internal_strlcpy(name, g.name, name_size);
+    region_address = g.beg;
+    region_size = g.size;
+  } else {
+    // region_{address,size} are already 0
+    region_kind = "heap-invalid";
+  }
+
+  CHECK(region_kind);
+  if (region_address_ptr) *region_address_ptr = region_address;
+  if (region_size_ptr) *region_size_ptr = region_size;
+  return region_kind;
 }
 
 SANITIZER_INTERFACE_ATTRIBUTE
diff --git a/lib/asan/asan_descriptions.cc b/lib/asan/asan_descriptions.cc
index 9b39360..7744cbf 100644
--- a/lib/asan/asan_descriptions.cc
+++ b/lib/asan/asan_descriptions.cc
@@ -14,6 +14,8 @@
 
 #include "asan_descriptions.h"
 #include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
 
 namespace __asan {
@@ -86,8 +88,7 @@
 bool DescribeAddressIfShadow(uptr addr) {
   ShadowAddressDescription descr;
   if (!GetShadowAddressInformation(addr, &descr)) return false;
-  Printf("Address %p is located in the %s area.\n", addr,
-         ShadowNames[descr.kind]);
+  descr.Print();
   return true;
 }
 
@@ -101,4 +102,385 @@
   return true;
 }
 
+// Heap descriptions
+static void GetAccessToHeapChunkInformation(ChunkAccess *descr,
+                                            AsanChunkView chunk, uptr addr,
+                                            uptr access_size) {
+  descr->bad_addr = addr;
+  if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {
+    descr->access_type = kAccessTypeLeft;
+  } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {
+    descr->access_type = kAccessTypeRight;
+    if (descr->offset < 0) {
+      descr->bad_addr -= descr->offset;
+      descr->offset = 0;
+    }
+  } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {
+    descr->access_type = kAccessTypeInside;
+  } else {
+    descr->access_type = kAccessTypeUnknown;
+  }
+  descr->chunk_begin = chunk.Beg();
+  descr->chunk_size = chunk.UsedSize();
+  descr->alloc_type = chunk.AllocType();
+}
+
+static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {
+  Decorator d;
+  InternalScopedString str(4096);
+  str.append("%s", d.Location());
+  switch (descr.access_type) {
+    case kAccessTypeLeft:
+      str.append("%p is located %zd bytes to the left of",
+                 (void *)descr.bad_addr, descr.offset);
+      break;
+    case kAccessTypeRight:
+      str.append("%p is located %zd bytes to the right of",
+                 (void *)descr.bad_addr, descr.offset);
+      break;
+    case kAccessTypeInside:
+      str.append("%p is located %zd bytes inside of", (void *)descr.bad_addr,
+                 descr.offset);
+      break;
+    case kAccessTypeUnknown:
+      str.append(
+          "%p is located somewhere around (this is AddressSanitizer bug!)",
+          (void *)descr.bad_addr);
+  }
+  str.append(" %zu-byte region [%p,%p)\n", descr.chunk_size,
+             (void *)descr.chunk_begin,
+             (void *)(descr.chunk_begin + descr.chunk_size));
+  str.append("%s", d.EndLocation());
+  Printf("%s", str.data());
+}
+
+bool GetHeapAddressInformation(uptr addr, uptr access_size,
+                               HeapAddressDescription *descr) {
+  AsanChunkView chunk = FindHeapChunkByAddress(addr);
+  if (!chunk.IsValid()) {
+    return false;
+  }
+  descr->addr = addr;
+  GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,
+                                  access_size);
+  CHECK_NE(chunk.AllocTid(), kInvalidTid);
+  descr->alloc_tid = chunk.AllocTid();
+  descr->alloc_stack_id = chunk.GetAllocStackId();
+  descr->free_tid = chunk.FreeTid();
+  if (descr->free_tid != kInvalidTid)
+    descr->free_stack_id = chunk.GetFreeStackId();
+  return true;
+}
+
+static StackTrace GetStackTraceFromId(u32 id) {
+  CHECK(id);
+  StackTrace res = StackDepotGet(id);
+  CHECK(res.trace);
+  return res;
+}
+
+bool DescribeAddressIfHeap(uptr addr, uptr access_size) {
+  HeapAddressDescription descr;
+  if (!GetHeapAddressInformation(addr, access_size, &descr)) {
+    Printf(
+        "AddressSanitizer can not describe address in more detail "
+        "(wild memory access suspected).\n");
+    return false;
+  }
+  descr.Print();
+  return true;
+}
+
+// Stack descriptions
+bool GetStackAddressInformation(uptr addr, uptr access_size,
+                                StackAddressDescription *descr) {
+  AsanThread *t = FindThreadByStackAddress(addr);
+  if (!t) return false;
+
+  descr->addr = addr;
+  descr->tid = t->tid();
+  // Try to fetch precise stack frame for this access.
+  AsanThread::StackFrameAccess access;
+  if (!t->GetStackFrameAccessByAddr(addr, &access)) {
+    descr->frame_descr = nullptr;
+    return true;
+  }
+
+  descr->offset = access.offset;
+  descr->access_size = access_size;
+  descr->frame_pc = access.frame_pc;
+  descr->frame_descr = access.frame_descr;
+
+#if SANITIZER_PPC64V1
+  // On PowerPC64 ELFv1, the address of a function actually points to a
+  // three-doubleword data structure with the first field containing
+  // the address of the function's code.
+  descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);
+#endif
+  descr->frame_pc += 16;
+
+  return true;
+}
+
+static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
+                                          uptr access_size, uptr prev_var_end,
+                                          uptr next_var_beg) {
+  uptr var_end = var.beg + var.size;
+  uptr addr_end = addr + access_size;
+  const char *pos_descr = nullptr;
+  // If the variable [var.beg, var_end) is the nearest variable to the
+  // current memory access, indicate it in the log.
+  if (addr >= var.beg) {
+    if (addr_end <= var_end)
+      pos_descr = "is inside";  // May happen if this is a use-after-return.
+    else if (addr < var_end)
+      pos_descr = "partially overflows";
+    else if (addr_end <= next_var_beg &&
+             next_var_beg - addr_end >= addr - var_end)
+      pos_descr = "overflows";
+  } else {
+    if (addr_end > var.beg)
+      pos_descr = "partially underflows";
+    else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)
+      pos_descr = "underflows";
+  }
+  InternalScopedString str(1024);
+  str.append("    [%zd, %zd)", var.beg, var_end);
+  // Render variable name.
+  str.append(" '");
+  for (uptr i = 0; i < var.name_len; ++i) {
+    str.append("%c", var.name_pos[i]);
+  }
+  str.append("'");
+  if (pos_descr) {
+    Decorator d;
+    // FIXME: we may want to also print the size of the access here,
+    // but in case of accesses generated by memset it may be confusing.
+    str.append("%s <== Memory access at offset %zd %s this variable%s\n",
+               d.Location(), addr, pos_descr, d.EndLocation());
+  } else {
+    str.append("\n");
+  }
+  Printf("%s", str.data());
+}
+
+bool DescribeAddressIfStack(uptr addr, uptr access_size) {
+  StackAddressDescription descr;
+  if (!GetStackAddressInformation(addr, access_size, &descr)) return false;
+  descr.Print();
+  return true;
+}
+
+// Global descriptions
+static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,
+                                            const __asan_global &g) {
+  InternalScopedString str(4096);
+  Decorator d;
+  str.append("%s", d.Location());
+  if (addr < g.beg) {
+    str.append("%p is located %zd bytes to the left", (void *)addr,
+               g.beg - addr);
+  } else if (addr + access_size > g.beg + g.size) {
+    if (addr < g.beg + g.size) addr = g.beg + g.size;
+    str.append("%p is located %zd bytes to the right", (void *)addr,
+               addr - (g.beg + g.size));
+  } else {
+    // Can it happen?
+    str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
+  }
+  str.append(" of global variable '%s' defined in '",
+             MaybeDemangleGlobalName(g.name));
+  PrintGlobalLocation(&str, g);
+  str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
+  str.append("%s", d.EndLocation());
+  PrintGlobalNameIfASCII(&str, g);
+  Printf("%s", str.data());
+}
+
+bool GetGlobalAddressInformation(uptr addr, uptr access_size,
+                                 GlobalAddressDescription *descr) {
+  descr->addr = addr;
+  int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,
+                                         ARRAY_SIZE(descr->globals));
+  descr->size = globals_num;
+  descr->access_size = access_size;
+  return globals_num != 0;
+}
+
+bool DescribeAddressIfGlobal(uptr addr, uptr access_size,
+                             const char *bug_type) {
+  GlobalAddressDescription descr;
+  if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;
+
+  descr.Print(bug_type);
+  return true;
+}
+
+void ShadowAddressDescription::Print() const {
+  Printf("Address %p is located in the %s area.\n", addr, ShadowNames[kind]);
+}
+
+void GlobalAddressDescription::Print(const char *bug_type) const {
+  for (int i = 0; i < size; i++) {
+    DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);
+    if (bug_type &&
+        0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
+        reg_sites[i]) {
+      Printf("  registered at:\n");
+      StackDepotGet(reg_sites[i]).Print();
+    }
+  }
+}
+
+void StackAddressDescription::Print() const {
+  Decorator d;
+  char tname[128];
+  Printf("%s", d.Location());
+  Printf("Address %p is located in stack of thread T%d%s", addr, tid,
+         ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+
+  if (!frame_descr) {
+    Printf("%s\n", d.EndLocation());
+    return;
+  }
+  Printf(" at offset %zu in frame%s\n", offset, d.EndLocation());
+
+  // Now we print the frame where the alloca has happened.
+  // We print this frame as a stack trace with one element.
+  // The symbolizer may print more than one frame if inlining was involved.
+  // The frame numbers may be different than those in the stack trace printed
+  // previously. That's unfortunate, but I have no better solution,
+  // especially given that the alloca may be from entirely different place
+  // (e.g. use-after-scope, or different thread's stack).
+  Printf("%s", d.EndLocation());
+  StackTrace alloca_stack(&frame_pc, 1);
+  alloca_stack.Print();
+
+  InternalMmapVector<StackVarDescr> vars(16);
+  if (!ParseFrameDescription(frame_descr, &vars)) {
+    Printf(
+        "AddressSanitizer can't parse the stack frame "
+        "descriptor: |%s|\n",
+        frame_descr);
+    // 'addr' is a stack address, so return true even if we can't parse frame
+    return;
+  }
+  uptr n_objects = vars.size();
+  // Report the number of stack objects.
+  Printf("  This frame has %zu object(s):\n", n_objects);
+
+  // Report all objects in this frame.
+  for (uptr i = 0; i < n_objects; i++) {
+    uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
+    uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
+    PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,
+                                  next_var_beg);
+  }
+  Printf(
+      "HINT: this may be a false positive if your program uses "
+      "some custom stack unwind mechanism or swapcontext\n");
+  if (SANITIZER_WINDOWS)
+    Printf("      (longjmp, SEH and C++ exceptions *are* supported)\n");
+  else
+    Printf("      (longjmp and C++ exceptions *are* supported)\n");
+
+  DescribeThread(GetThreadContextByTidLocked(tid));
+}
+
+void HeapAddressDescription::Print() const {
+  PrintHeapChunkAccess(addr, chunk_access);
+
+  asanThreadRegistry().CheckLocked();
+  AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);
+  StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);
+
+  char tname[128];
+  Decorator d;
+  AsanThreadContext *free_thread = nullptr;
+  if (free_tid != kInvalidTid) {
+    free_thread = GetThreadContextByTidLocked(free_tid);
+    Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
+           free_thread->tid,
+           ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
+           d.EndAllocation());
+    StackTrace free_stack = GetStackTraceFromId(free_stack_id);
+    free_stack.Print();
+    Printf("%spreviously allocated by thread T%d%s here:%s\n", d.Allocation(),
+           alloc_thread->tid,
+           ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+           d.EndAllocation());
+  } else {
+    Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
+           alloc_thread->tid,
+           ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
+           d.EndAllocation());
+  }
+  alloc_stack.Print();
+  DescribeThread(GetCurrentThread());
+  if (free_thread) DescribeThread(free_thread);
+  DescribeThread(alloc_thread);
+}
+
+AddressDescription::AddressDescription(uptr addr, uptr access_size,
+                                       bool shouldLockThreadRegistry) {
+  if (GetShadowAddressInformation(addr, &data.shadow)) {
+    data.kind = kAddressKindShadow;
+    return;
+  }
+  if (GetHeapAddressInformation(addr, access_size, &data.heap)) {
+    data.kind = kAddressKindHeap;
+    return;
+  }
+
+  bool isStackMemory = false;
+  if (shouldLockThreadRegistry) {
+    ThreadRegistryLock l(&asanThreadRegistry());
+    isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
+  } else {
+    isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);
+  }
+  if (isStackMemory) {
+    data.kind = kAddressKindStack;
+    return;
+  }
+
+  if (GetGlobalAddressInformation(addr, access_size, &data.global)) {
+    data.kind = kAddressKindGlobal;
+    return;
+  }
+  data.kind = kAddressKindWild;
+  addr = 0;
+}
+
+void PrintAddressDescription(uptr addr, uptr access_size,
+                             const char *bug_type) {
+  ShadowAddressDescription shadow_descr;
+  if (GetShadowAddressInformation(addr, &shadow_descr)) {
+    shadow_descr.Print();
+    return;
+  }
+
+  GlobalAddressDescription global_descr;
+  if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {
+    global_descr.Print(bug_type);
+    return;
+  }
+
+  StackAddressDescription stack_descr;
+  if (GetStackAddressInformation(addr, access_size, &stack_descr)) {
+    stack_descr.Print();
+    return;
+  }
+
+  HeapAddressDescription heap_descr;
+  if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {
+    heap_descr.Print();
+    return;
+  }
+
+  // We exhausted our possibilities. Bail out.
+  Printf(
+      "AddressSanitizer can not describe address in more detail "
+      "(wild memory access suspected).\n");
+}
 }  // namespace __asan
diff --git a/lib/asan/asan_descriptions.h b/lib/asan/asan_descriptions.h
index e93adec..0ee677e 100644
--- a/lib/asan/asan_descriptions.h
+++ b/lib/asan/asan_descriptions.h
@@ -12,8 +12,10 @@
 // ASan-private header for asan_descriptions.cc.
 // TODO(filcab): Most struct definitions should move to the interface headers.
 //===----------------------------------------------------------------------===//
+#ifndef ASAN_DESCRIPTIONS_H
+#define ASAN_DESCRIPTIONS_H
 
-#include "asan_internal.h"
+#include "asan_allocator.h"
 #include "asan_thread.h"
 #include "sanitizer_common/sanitizer_common.h"
 #include "sanitizer_common/sanitizer_report_decorator.h"
@@ -41,7 +43,6 @@
   const char *ShadowByte(u8 byte) {
     switch (byte) {
       case kAsanHeapLeftRedzoneMagic:
-      case kAsanHeapRightRedzoneMagic:
       case kAsanArrayCookieMagic:
         return Red();
       case kAsanHeapFreeMagic:
@@ -49,7 +50,6 @@
       case kAsanStackLeftRedzoneMagic:
       case kAsanStackMidRedzoneMagic:
       case kAsanStackRightRedzoneMagic:
-      case kAsanStackPartialRedzoneMagic:
         return Red();
       case kAsanStackAfterReturnMagic:
         return Magenta();
@@ -89,9 +89,165 @@
   uptr addr;
   ShadowKind kind;
   u8 shadow_byte;
+
+  void Print() const;
 };
 
 bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr);
 bool DescribeAddressIfShadow(uptr addr);
 
+enum AccessType {
+  kAccessTypeLeft,
+  kAccessTypeRight,
+  kAccessTypeInside,
+  kAccessTypeUnknown,  // This means we have an AddressSanitizer bug!
+};
+
+struct ChunkAccess {
+  uptr bad_addr;
+  sptr offset;
+  uptr chunk_begin;
+  uptr chunk_size;
+  u32 access_type : 2;
+  u32 alloc_type : 2;
+};
+
+struct HeapAddressDescription {
+  uptr addr;
+  uptr alloc_tid;
+  uptr free_tid;
+  u32 alloc_stack_id;
+  u32 free_stack_id;
+  ChunkAccess chunk_access;
+
+  void Print() const;
+};
+
+bool GetHeapAddressInformation(uptr addr, uptr access_size,
+                               HeapAddressDescription *descr);
+bool DescribeAddressIfHeap(uptr addr, uptr access_size = 1);
+
+struct StackAddressDescription {
+  uptr addr;
+  uptr tid;
+  uptr offset;
+  uptr frame_pc;
+  uptr access_size;
+  const char *frame_descr;
+
+  void Print() const;
+};
+
+bool GetStackAddressInformation(uptr addr, uptr access_size,
+                                StackAddressDescription *descr);
+
+struct GlobalAddressDescription {
+  uptr addr;
+  // Assume address is close to at most four globals.
+  static const int kMaxGlobals = 4;
+  __asan_global globals[kMaxGlobals];
+  u32 reg_sites[kMaxGlobals];
+  uptr access_size;
+  u8 size;
+
+  void Print(const char *bug_type = "") const;
+};
+
+bool GetGlobalAddressInformation(uptr addr, uptr access_size,
+                                 GlobalAddressDescription *descr);
+bool DescribeAddressIfGlobal(uptr addr, uptr access_size, const char *bug_type);
+
+// General function to describe an address. Will try to describe the address as
+// a shadow, global (variable), stack, or heap address.
+// bug_type is optional and is used for checking if we're reporting an
+// initialization-order-fiasco
+// The proper access_size should be passed for stack, global, and heap
+// addresses. Defaults to 1.
+// Each of the *AddressDescription functions has its own Print() member, which
+// may take access_size and bug_type parameters if needed.
+void PrintAddressDescription(uptr addr, uptr access_size = 1,
+                             const char *bug_type = "");
+
+enum AddressKind {
+  kAddressKindWild,
+  kAddressKindShadow,
+  kAddressKindHeap,
+  kAddressKindStack,
+  kAddressKindGlobal,
+};
+
+class AddressDescription {
+  struct AddressDescriptionData {
+    AddressKind kind;
+    union {
+      ShadowAddressDescription shadow;
+      HeapAddressDescription heap;
+      StackAddressDescription stack;
+      GlobalAddressDescription global;
+      uptr addr;
+    };
+  };
+
+  AddressDescriptionData data;
+
+ public:
+  AddressDescription() = default;
+  // shouldLockThreadRegistry allows us to skip locking if we're sure we already
+  // have done it.
+  AddressDescription(uptr addr, bool shouldLockThreadRegistry = true)
+      : AddressDescription(addr, 1, shouldLockThreadRegistry) {}
+  AddressDescription(uptr addr, uptr access_size,
+                     bool shouldLockThreadRegistry = true);
+
+  uptr Address() const {
+    switch (data.kind) {
+      case kAddressKindWild:
+        return data.addr;
+      case kAddressKindShadow:
+        return data.shadow.addr;
+      case kAddressKindHeap:
+        return data.heap.addr;
+      case kAddressKindStack:
+        return data.stack.addr;
+      case kAddressKindGlobal:
+        return data.global.addr;
+    }
+    UNREACHABLE("AddressInformation kind is invalid");
+  }
+  void Print(const char *bug_descr = nullptr) const {
+    switch (data.kind) {
+      case kAddressKindWild:
+        Printf("Address %p is a wild pointer.\n", data.addr);
+        return;
+      case kAddressKindShadow:
+        return data.shadow.Print();
+      case kAddressKindHeap:
+        return data.heap.Print();
+      case kAddressKindStack:
+        return data.stack.Print();
+      case kAddressKindGlobal:
+        // initialization-order-fiasco has a special Print()
+        return data.global.Print(bug_descr);
+    }
+    UNREACHABLE("AddressInformation kind is invalid");
+  }
+
+  void StoreTo(AddressDescriptionData *dst) const { *dst = data; }
+
+  const ShadowAddressDescription *AsShadow() const {
+    return data.kind == kAddressKindShadow ? &data.shadow : nullptr;
+  }
+  const HeapAddressDescription *AsHeap() const {
+    return data.kind == kAddressKindHeap ? &data.heap : nullptr;
+  }
+  const StackAddressDescription *AsStack() const {
+    return data.kind == kAddressKindStack ? &data.stack : nullptr;
+  }
+  const GlobalAddressDescription *AsGlobal() const {
+    return data.kind == kAddressKindGlobal ? &data.global : nullptr;
+  }
+};
+
 }  // namespace __asan
+
+#endif  // ASAN_DESCRIPTIONS_H
diff --git a/lib/asan/asan_errors.cc b/lib/asan/asan_errors.cc
new file mode 100644
index 0000000..a571a71
--- /dev/null
+++ b/lib/asan/asan_errors.cc
@@ -0,0 +1,496 @@
+//===-- asan_errors.cc ------------------------------------------*- C++ -*-===//
+//
+//                     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.
+//
+// ASan implementation for error structures.
+//===----------------------------------------------------------------------===//
+
+#include "asan_errors.h"
+#include <signal.h>
+#include "asan_descriptions.h"
+#include "asan_mapping.h"
+#include "asan_report.h"
+#include "asan_stack.h"
+#include "sanitizer_common/sanitizer_stackdepot.h"
+
+namespace __asan {
+
+void ErrorStackOverflow::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report(
+      "ERROR: AddressSanitizer: stack-overflow on address %p"
+      " (pc %p bp %p sp %p T%d)\n",
+      (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+  BufferedStackTrace stack;
+  GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
+                                  common_flags()->fast_unwind_on_fatal);
+  stack.Print();
+  ReportErrorSummary("stack-overflow", &stack);
+}
+
+static void MaybeDumpInstructionBytes(uptr pc) {
+  if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) return;
+  InternalScopedString str(1024);
+  str.append("First 16 instruction bytes at pc: ");
+  if (IsAccessibleMemoryRange(pc, 16)) {
+    for (int i = 0; i < 16; ++i) {
+      PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/ false, " ");
+    }
+    str.append("\n");
+  } else {
+    str.append("unaccessible\n");
+  }
+  Report("%s", str.data());
+}
+
+void ErrorDeadlySignal::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  const char *description = DescribeSignalOrException(signo);
+  Report(
+      "ERROR: AddressSanitizer: %s on unknown address %p (pc %p bp %p sp %p "
+      "T%d)\n",
+      description, (void *)addr, (void *)pc, (void *)bp, (void *)sp, tid);
+  Printf("%s", d.EndWarning());
+  if (pc < GetPageSizeCached()) Report("Hint: pc points to the zero page.\n");
+  if (is_memory_access) {
+    const char *access_type =
+        write_flag == SignalContext::WRITE
+            ? "WRITE"
+            : (write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
+    Report("The signal is caused by a %s memory access.\n", access_type);
+    if (addr < GetPageSizeCached())
+      Report("Hint: address points to the zero page.\n");
+  }
+  scariness.Print();
+  BufferedStackTrace stack;
+  GetStackTraceWithPcBpAndContext(&stack, kStackTraceMax, pc, bp, context,
+                                  common_flags()->fast_unwind_on_fatal);
+  stack.Print();
+  MaybeDumpInstructionBytes(pc);
+  Printf("AddressSanitizer can not provide additional info.\n");
+  ReportErrorSummary(description, &stack);
+}
+
+void ErrorDoubleFree::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  char tname[128];
+  Report(
+      "ERROR: AddressSanitizer: attempting double-free on %p in "
+      "thread T%d%s:\n",
+      addr_description.addr, tid,
+      ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+  GET_STACK_TRACE_FATAL(second_free_stack->trace[0],
+                        second_free_stack->top_frame_bp);
+  stack.Print();
+  addr_description.Print();
+  ReportErrorSummary("double-free", &stack);
+}
+
+void ErrorNewDeleteSizeMismatch::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  char tname[128];
+  Report(
+      "ERROR: AddressSanitizer: new-delete-type-mismatch on %p in thread "
+      "T%d%s:\n",
+      addr_description.addr, tid,
+      ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+  Printf("%s  object passed to delete has wrong type:\n", d.EndWarning());
+  Printf(
+      "  size of the allocated type:   %zd bytes;\n"
+      "  size of the deallocated type: %zd bytes.\n",
+      addr_description.chunk_access.chunk_size, delete_size);
+  CHECK_GT(free_stack->size, 0);
+  scariness.Print();
+  GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+  stack.Print();
+  addr_description.Print();
+  ReportErrorSummary("new-delete-type-mismatch", &stack);
+  Report(
+      "HINT: if you don't care about these errors you may set "
+      "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+}
+
+void ErrorFreeNotMalloced::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  char tname[128];
+  Report(
+      "ERROR: AddressSanitizer: attempting free on address "
+      "which was not malloc()-ed: %p in thread T%d%s\n",
+      addr_description.Address(), tid,
+      ThreadNameWithParenthesis(tid, tname, sizeof(tname)));
+  Printf("%s", d.EndWarning());
+  CHECK_GT(free_stack->size, 0);
+  scariness.Print();
+  GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
+  stack.Print();
+  addr_description.Print();
+  ReportErrorSummary("bad-free", &stack);
+}
+
+void ErrorAllocTypeMismatch::Print() {
+  static const char *alloc_names[] = {"INVALID", "malloc", "operator new",
+                                      "operator new []"};
+  static const char *dealloc_names[] = {"INVALID", "free", "operator delete",
+                                        "operator delete []"};
+  CHECK_NE(alloc_type, dealloc_type);
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
+         alloc_names[alloc_type], dealloc_names[dealloc_type],
+         addr_description.addr);
+  Printf("%s", d.EndWarning());
+  CHECK_GT(dealloc_stack->size, 0);
+  scariness.Print();
+  GET_STACK_TRACE_FATAL(dealloc_stack->trace[0], dealloc_stack->top_frame_bp);
+  stack.Print();
+  addr_description.Print();
+  ReportErrorSummary("alloc-dealloc-mismatch", &stack);
+  Report(
+      "HINT: if you don't care about these errors you may set "
+      "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+}
+
+void ErrorMallocUsableSizeNotOwned::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report(
+      "ERROR: AddressSanitizer: attempting to call malloc_usable_size() for "
+      "pointer which is not owned: %p\n",
+      addr_description.Address());
+  Printf("%s", d.EndWarning());
+  stack->Print();
+  addr_description.Print();
+  ReportErrorSummary("bad-malloc_usable_size", stack);
+}
+
+void ErrorSanitizerGetAllocatedSizeNotOwned::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report(
+      "ERROR: AddressSanitizer: attempting to call "
+      "__sanitizer_get_allocated_size() for pointer which is not owned: %p\n",
+      addr_description.Address());
+  Printf("%s", d.EndWarning());
+  stack->Print();
+  addr_description.Print();
+  ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
+}
+
+void ErrorStringFunctionMemoryRangesOverlap::Print() {
+  Decorator d;
+  char bug_type[100];
+  internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
+  Printf("%s", d.Warning());
+  Report(
+      "ERROR: AddressSanitizer: %s: memory ranges [%p,%p) and [%p, %p) "
+      "overlap\n",
+      bug_type, addr1_description.Address(),
+      addr1_description.Address() + length1, addr2_description.Address(),
+      addr2_description.Address() + length2);
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+  stack->Print();
+  addr1_description.Print();
+  addr2_description.Print();
+  ReportErrorSummary(bug_type, stack);
+}
+
+void ErrorStringFunctionSizeOverflow::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  const char *bug_type = "negative-size-param";
+  Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
+  Printf("%s", d.EndWarning());
+  scariness.Print();
+  stack->Print();
+  addr_description.Print();
+  ReportErrorSummary(bug_type, stack);
+}
+
+void ErrorBadParamsToAnnotateContiguousContainer::Print() {
+  Report(
+      "ERROR: AddressSanitizer: bad parameters to "
+      "__sanitizer_annotate_contiguous_container:\n"
+      "      beg     : %p\n"
+      "      end     : %p\n"
+      "      old_mid : %p\n"
+      "      new_mid : %p\n",
+      beg, end, old_mid, new_mid);
+  uptr granularity = SHADOW_GRANULARITY;
+  if (!IsAligned(beg, granularity))
+    Report("ERROR: beg is not aligned by %d\n", granularity);
+  stack->Print();
+  ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
+}
+
+void ErrorODRViolation::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report("ERROR: AddressSanitizer: odr-violation (%p):\n", global1.beg);
+  Printf("%s", d.EndWarning());
+  InternalScopedString g1_loc(256), g2_loc(256);
+  PrintGlobalLocation(&g1_loc, global1);
+  PrintGlobalLocation(&g2_loc, global2);
+  Printf("  [1] size=%zd '%s' %s\n", global1.size,
+         MaybeDemangleGlobalName(global1.name), g1_loc.data());
+  Printf("  [2] size=%zd '%s' %s\n", global2.size,
+         MaybeDemangleGlobalName(global2.name), g2_loc.data());
+  if (stack_id1 && stack_id2) {
+    Printf("These globals were registered at these points:\n");
+    Printf("  [1]:\n");
+    StackDepotGet(stack_id1).Print();
+    Printf("  [2]:\n");
+    StackDepotGet(stack_id2).Print();
+  }
+  Report(
+      "HINT: if you don't care about these errors you may set "
+      "ASAN_OPTIONS=detect_odr_violation=0\n");
+  InternalScopedString error_msg(256);
+  error_msg.append("odr-violation: global '%s' at %s",
+                   MaybeDemangleGlobalName(global1.name), g1_loc.data());
+  ReportErrorSummary(error_msg.data());
+}
+
+void ErrorInvalidPointerPair::Print() {
+  const char *bug_type = "invalid-pointer-pair";
+  Decorator d;
+  Printf("%s", d.Warning());
+  Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n",
+         addr1_description.Address(), addr2_description.Address());
+  Printf("%s", d.EndWarning());
+  GET_STACK_TRACE_FATAL(pc, bp);
+  stack.Print();
+  addr1_description.Print();
+  addr2_description.Print();
+  ReportErrorSummary(bug_type, &stack);
+}
+
+static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
+  return s[-1] > 127 && s[1] > 127;
+}
+
+ErrorGeneric::ErrorGeneric(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr addr,
+                           bool is_write_, uptr access_size_)
+    : ErrorBase(tid),
+      addr_description(addr, access_size_, /*shouldLockThreadRegistry=*/false),
+      pc(pc_),
+      bp(bp_),
+      sp(sp_),
+      access_size(access_size_),
+      is_write(is_write_),
+      shadow_val(0) {
+  scariness.Clear();
+  if (access_size) {
+    if (access_size <= 9) {
+      char desr[] = "?-byte";
+      desr[0] = '0' + access_size;
+      scariness.Scare(access_size + access_size / 2, desr);
+    } else if (access_size >= 10) {
+      scariness.Scare(15, "multi-byte");
+    }
+    is_write ? scariness.Scare(20, "write") : scariness.Scare(1, "read");
+
+    // Determine the error type.
+    bug_descr = "unknown-crash";
+    if (AddrIsInMem(addr)) {
+      u8 *shadow_addr = (u8 *)MemToShadow(addr);
+      // If we are accessing 16 bytes, look at the second shadow byte.
+      if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY) shadow_addr++;
+      // If we are in the partial right redzone, look at the next shadow byte.
+      if (*shadow_addr > 0 && *shadow_addr < 128) shadow_addr++;
+      bool far_from_bounds = false;
+      shadow_val = *shadow_addr;
+      int bug_type_score = 0;
+      // For use-after-frees reads are almost as bad as writes.
+      int read_after_free_bonus = 0;
+      switch (shadow_val) {
+        case kAsanHeapLeftRedzoneMagic:
+        case kAsanArrayCookieMagic:
+          bug_descr = "heap-buffer-overflow";
+          bug_type_score = 10;
+          far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+          break;
+        case kAsanHeapFreeMagic:
+          bug_descr = "heap-use-after-free";
+          bug_type_score = 20;
+          if (!is_write) read_after_free_bonus = 18;
+          break;
+        case kAsanStackLeftRedzoneMagic:
+          bug_descr = "stack-buffer-underflow";
+          bug_type_score = 25;
+          far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+          break;
+        case kAsanInitializationOrderMagic:
+          bug_descr = "initialization-order-fiasco";
+          bug_type_score = 1;
+          break;
+        case kAsanStackMidRedzoneMagic:
+        case kAsanStackRightRedzoneMagic:
+          bug_descr = "stack-buffer-overflow";
+          bug_type_score = 25;
+          far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+          break;
+        case kAsanStackAfterReturnMagic:
+          bug_descr = "stack-use-after-return";
+          bug_type_score = 30;
+          if (!is_write) read_after_free_bonus = 18;
+          break;
+        case kAsanUserPoisonedMemoryMagic:
+          bug_descr = "use-after-poison";
+          bug_type_score = 20;
+          break;
+        case kAsanContiguousContainerOOBMagic:
+          bug_descr = "container-overflow";
+          bug_type_score = 10;
+          break;
+        case kAsanStackUseAfterScopeMagic:
+          bug_descr = "stack-use-after-scope";
+          bug_type_score = 10;
+          break;
+        case kAsanGlobalRedzoneMagic:
+          bug_descr = "global-buffer-overflow";
+          bug_type_score = 10;
+          far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+          break;
+        case kAsanIntraObjectRedzone:
+          bug_descr = "intra-object-overflow";
+          bug_type_score = 10;
+          break;
+        case kAsanAllocaLeftMagic:
+        case kAsanAllocaRightMagic:
+          bug_descr = "dynamic-stack-buffer-overflow";
+          bug_type_score = 25;
+          far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
+          break;
+      }
+      scariness.Scare(bug_type_score + read_after_free_bonus, bug_descr);
+      if (far_from_bounds) scariness.Scare(10, "far-from-bounds");
+    }
+  }
+}
+
+static void PrintContainerOverflowHint() {
+  Printf("HINT: if you don't care about these errors you may set "
+         "ASAN_OPTIONS=detect_container_overflow=0.\n"
+         "If you suspect a false positive see also: "
+         "https://github.com/google/sanitizers/wiki/"
+         "AddressSanitizerContainerOverflow.\n");
+}
+
+static void PrintShadowByte(InternalScopedString *str, const char *before,
+    u8 byte, const char *after = "\n") {
+  PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
+}
+
+static void PrintLegend(InternalScopedString *str) {
+  str->append(
+      "Shadow byte legend (one shadow byte represents %d "
+      "application bytes):\n",
+      (int)SHADOW_GRANULARITY);
+  PrintShadowByte(str, "  Addressable:           ", 0);
+  str->append("  Partially addressable: ");
+  for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
+  str->append("\n");
+  PrintShadowByte(str, "  Heap left redzone:       ",
+                  kAsanHeapLeftRedzoneMagic);
+  PrintShadowByte(str, "  Freed heap region:       ", kAsanHeapFreeMagic);
+  PrintShadowByte(str, "  Stack left redzone:      ",
+                  kAsanStackLeftRedzoneMagic);
+  PrintShadowByte(str, "  Stack mid redzone:       ",
+                  kAsanStackMidRedzoneMagic);
+  PrintShadowByte(str, "  Stack right redzone:     ",
+                  kAsanStackRightRedzoneMagic);
+  PrintShadowByte(str, "  Stack after return:      ",
+                  kAsanStackAfterReturnMagic);
+  PrintShadowByte(str, "  Stack use after scope:   ",
+                  kAsanStackUseAfterScopeMagic);
+  PrintShadowByte(str, "  Global redzone:          ", kAsanGlobalRedzoneMagic);
+  PrintShadowByte(str, "  Global init order:       ",
+                  kAsanInitializationOrderMagic);
+  PrintShadowByte(str, "  Poisoned by user:        ",
+                  kAsanUserPoisonedMemoryMagic);
+  PrintShadowByte(str, "  Container overflow:      ",
+                  kAsanContiguousContainerOOBMagic);
+  PrintShadowByte(str, "  Array cookie:            ",
+                  kAsanArrayCookieMagic);
+  PrintShadowByte(str, "  Intra object redzone:    ",
+                  kAsanIntraObjectRedzone);
+  PrintShadowByte(str, "  ASan internal:           ", kAsanInternalHeapMagic);
+  PrintShadowByte(str, "  Left alloca redzone:     ", kAsanAllocaLeftMagic);
+  PrintShadowByte(str, "  Right alloca redzone:    ", kAsanAllocaRightMagic);
+}
+
+static void PrintShadowBytes(InternalScopedString *str, const char *before,
+                             u8 *bytes, u8 *guilty, uptr n) {
+  Decorator d;
+  if (before) str->append("%s%p:", before, bytes);
+  for (uptr i = 0; i < n; i++) {
+    u8 *p = bytes + i;
+    const char *before =
+        p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
+    const char *after = p == guilty ? "]" : "";
+    PrintShadowByte(str, before, *p, after);
+  }
+  str->append("\n");
+}
+
+static void PrintShadowMemoryForAddress(uptr addr) {
+  if (!AddrIsInMem(addr)) return;
+  uptr shadow_addr = MemToShadow(addr);
+  const uptr n_bytes_per_row = 16;
+  uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
+  InternalScopedString str(4096 * 8);
+  str.append("Shadow bytes around the buggy address:\n");
+  for (int i = -5; i <= 5; i++) {
+    const char *prefix = (i == 0) ? "=>" : "  ";
+    PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
+                     (u8 *)shadow_addr, n_bytes_per_row);
+  }
+  if (flags()->print_legend) PrintLegend(&str);
+  Printf("%s", str.data());
+}
+
+void ErrorGeneric::Print() {
+  Decorator d;
+  Printf("%s", d.Warning());
+  uptr addr = addr_description.Address();
+  Report("ERROR: AddressSanitizer: %s on address %p at pc %p bp %p sp %p\n",
+         bug_descr, (void *)addr, pc, bp, sp);
+  Printf("%s", d.EndWarning());
+
+  char tname[128];
+  Printf("%s%s of size %zu at %p thread T%d%s%s\n", d.Access(),
+         access_size ? (is_write ? "WRITE" : "READ") : "ACCESS", access_size,
+         (void *)addr, tid,
+         ThreadNameWithParenthesis(tid, tname, sizeof(tname)), d.EndAccess());
+
+  scariness.Print();
+  GET_STACK_TRACE_FATAL(pc, bp);
+  stack.Print();
+
+  // Pass bug_descr because we have a special case for
+  // initialization-order-fiasco
+  addr_description.Print(bug_descr);
+  if (shadow_val == kAsanContiguousContainerOOBMagic)
+    PrintContainerOverflowHint();
+  ReportErrorSummary(bug_descr, &stack);
+  PrintShadowMemoryForAddress(addr);
+}
+
+}  // namespace __asan
diff --git a/lib/asan/asan_errors.h b/lib/asan/asan_errors.h
new file mode 100644
index 0000000..cfd58ab
--- /dev/null
+++ b/lib/asan/asan_errors.h
@@ -0,0 +1,378 @@
+//===-- asan_errors.h -------------------------------------------*- C++ -*-===//
+//
+//                     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.
+//
+// ASan-private header for error structures.
+//===----------------------------------------------------------------------===//
+#ifndef ASAN_ERRORS_H
+#define ASAN_ERRORS_H
+
+#include "asan_descriptions.h"
+#include "asan_scariness_score.h"
+#include "sanitizer_common/sanitizer_common.h"
+
+namespace __asan {
+
+struct ErrorBase {
+  ErrorBase() = default;
+  explicit ErrorBase(u32 tid_) : tid(tid_) {}
+  ScarinessScoreBase scariness;
+  u32 tid;
+};
+
+struct ErrorStackOverflow : ErrorBase {
+  uptr addr, pc, bp, sp;
+  // ErrorStackOverflow never owns the context.
+  void *context;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorStackOverflow() = default;
+  ErrorStackOverflow(u32 tid, const SignalContext &sig)
+      : ErrorBase(tid),
+        addr(sig.addr),
+        pc(sig.pc),
+        bp(sig.bp),
+        sp(sig.sp),
+        context(sig.context) {
+    scariness.Clear();
+    scariness.Scare(10, "stack-overflow");
+  }
+  void Print();
+};
+
+struct ErrorDeadlySignal : ErrorBase {
+  uptr addr, pc, bp, sp;
+  // ErrorDeadlySignal never owns the context.
+  void *context;
+  int signo;
+  SignalContext::WriteFlag write_flag;
+  bool is_memory_access;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorDeadlySignal() = default;
+  ErrorDeadlySignal(u32 tid, const SignalContext &sig, int signo_)
+      : ErrorBase(tid),
+        addr(sig.addr),
+        pc(sig.pc),
+        bp(sig.bp),
+        sp(sig.sp),
+        context(sig.context),
+        signo(signo_),
+        write_flag(sig.write_flag),
+        is_memory_access(sig.is_memory_access) {
+    scariness.Clear();
+    if (is_memory_access) {
+      if (addr < GetPageSizeCached()) {
+        scariness.Scare(10, "null-deref");
+      } else if (addr == pc) {
+        scariness.Scare(60, "wild-jump");
+      } else if (write_flag == SignalContext::WRITE) {
+        scariness.Scare(30, "wild-addr-write");
+      } else if (write_flag == SignalContext::READ) {
+        scariness.Scare(20, "wild-addr-read");
+      } else {
+        scariness.Scare(25, "wild-addr");
+      }
+    } else {
+      scariness.Scare(10, "signal");
+    }
+  }
+  void Print();
+};
+
+struct ErrorDoubleFree : ErrorBase {
+  // ErrorDoubleFree doesn't own the stack trace.
+  const BufferedStackTrace *second_free_stack;
+  HeapAddressDescription addr_description;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorDoubleFree() = default;
+  ErrorDoubleFree(u32 tid, BufferedStackTrace *stack, uptr addr)
+      : ErrorBase(tid), second_free_stack(stack) {
+    CHECK_GT(second_free_stack->size, 0);
+    GetHeapAddressInformation(addr, 1, &addr_description);
+    scariness.Clear();
+    scariness.Scare(42, "double-free");
+  }
+  void Print();
+};
+
+struct ErrorNewDeleteSizeMismatch : ErrorBase {
+  // ErrorNewDeleteSizeMismatch doesn't own the stack trace.
+  const BufferedStackTrace *free_stack;
+  HeapAddressDescription addr_description;
+  uptr delete_size;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorNewDeleteSizeMismatch() = default;
+  ErrorNewDeleteSizeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
+                             uptr delete_size_)
+      : ErrorBase(tid), free_stack(stack), delete_size(delete_size_) {
+    GetHeapAddressInformation(addr, 1, &addr_description);
+    scariness.Clear();
+    scariness.Scare(10, "new-delete-type-mismatch");
+  }
+  void Print();
+};
+
+struct ErrorFreeNotMalloced : ErrorBase {
+  // ErrorFreeNotMalloced doesn't own the stack trace.
+  const BufferedStackTrace *free_stack;
+  AddressDescription addr_description;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorFreeNotMalloced() = default;
+  ErrorFreeNotMalloced(u32 tid, BufferedStackTrace *stack, uptr addr)
+      : ErrorBase(tid),
+        free_stack(stack),
+        addr_description(addr, /*shouldLockThreadRegistry=*/false) {
+    scariness.Clear();
+    scariness.Scare(40, "bad-free");
+  }
+  void Print();
+};
+
+struct ErrorAllocTypeMismatch : ErrorBase {
+  // ErrorAllocTypeMismatch doesn't own the stack trace.
+  const BufferedStackTrace *dealloc_stack;
+  HeapAddressDescription addr_description;
+  AllocType alloc_type, dealloc_type;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorAllocTypeMismatch() = default;
+  ErrorAllocTypeMismatch(u32 tid, BufferedStackTrace *stack, uptr addr,
+                         AllocType alloc_type_, AllocType dealloc_type_)
+      : ErrorBase(tid),
+        dealloc_stack(stack),
+        alloc_type(alloc_type_),
+        dealloc_type(dealloc_type_) {
+    GetHeapAddressInformation(addr, 1, &addr_description);
+    scariness.Clear();
+    scariness.Scare(10, "alloc-dealloc-mismatch");
+  };
+  void Print();
+};
+
+struct ErrorMallocUsableSizeNotOwned : ErrorBase {
+  // ErrorMallocUsableSizeNotOwned doesn't own the stack trace.
+  const BufferedStackTrace *stack;
+  AddressDescription addr_description;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorMallocUsableSizeNotOwned() = default;
+  ErrorMallocUsableSizeNotOwned(u32 tid, BufferedStackTrace *stack_, uptr addr)
+      : ErrorBase(tid),
+        stack(stack_),
+        addr_description(addr, /*shouldLockThreadRegistry=*/false) {
+    scariness.Clear();
+  }
+  void Print();
+};
+
+struct ErrorSanitizerGetAllocatedSizeNotOwned : ErrorBase {
+  // ErrorSanitizerGetAllocatedSizeNotOwned doesn't own the stack trace.
+  const BufferedStackTrace *stack;
+  AddressDescription addr_description;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorSanitizerGetAllocatedSizeNotOwned() = default;
+  ErrorSanitizerGetAllocatedSizeNotOwned(u32 tid, BufferedStackTrace *stack_,
+                                         uptr addr)
+      : ErrorBase(tid),
+        stack(stack_),
+        addr_description(addr, /*shouldLockThreadRegistry=*/false) {
+    scariness.Clear();
+  }
+  void Print();
+};
+
+struct ErrorStringFunctionMemoryRangesOverlap : ErrorBase {
+  // ErrorStringFunctionMemoryRangesOverlap doesn't own the stack trace.
+  const BufferedStackTrace *stack;
+  uptr length1, length2;
+  AddressDescription addr1_description;
+  AddressDescription addr2_description;
+  const char *function;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorStringFunctionMemoryRangesOverlap() = default;
+  ErrorStringFunctionMemoryRangesOverlap(u32 tid, BufferedStackTrace *stack_,
+                                         uptr addr1, uptr length1_, uptr addr2,
+                                         uptr length2_, const char *function_)
+      : ErrorBase(tid),
+        stack(stack_),
+        length1(length1_),
+        length2(length2_),
+        addr1_description(addr1, length1, /*shouldLockThreadRegistry=*/false),
+        addr2_description(addr2, length2, /*shouldLockThreadRegistry=*/false),
+        function(function_) {
+    char bug_type[100];
+    internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
+    scariness.Clear();
+    scariness.Scare(10, bug_type);
+  }
+  void Print();
+};
+
+struct ErrorStringFunctionSizeOverflow : ErrorBase {
+  // ErrorStringFunctionSizeOverflow doesn't own the stack trace.
+  const BufferedStackTrace *stack;
+  AddressDescription addr_description;
+  uptr size;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorStringFunctionSizeOverflow() = default;
+  ErrorStringFunctionSizeOverflow(u32 tid, BufferedStackTrace *stack_,
+                                  uptr addr, uptr size_)
+      : ErrorBase(tid),
+        stack(stack_),
+        addr_description(addr, /*shouldLockThreadRegistry=*/false),
+        size(size_) {
+    scariness.Clear();
+    scariness.Scare(10, "negative-size-param");
+  }
+  void Print();
+};
+
+struct ErrorBadParamsToAnnotateContiguousContainer : ErrorBase {
+  // ErrorBadParamsToAnnotateContiguousContainer doesn't own the stack trace.
+  const BufferedStackTrace *stack;
+  uptr beg, end, old_mid, new_mid;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorBadParamsToAnnotateContiguousContainer() = default;
+  // PS4: Do we want an AddressDescription for beg?
+  ErrorBadParamsToAnnotateContiguousContainer(u32 tid,
+                                              BufferedStackTrace *stack_,
+                                              uptr beg_, uptr end_,
+                                              uptr old_mid_, uptr new_mid_)
+      : ErrorBase(tid),
+        stack(stack_),
+        beg(beg_),
+        end(end_),
+        old_mid(old_mid_),
+        new_mid(new_mid_) {}
+  void Print();
+};
+
+struct ErrorODRViolation : ErrorBase {
+  __asan_global global1, global2;
+  u32 stack_id1, stack_id2;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorODRViolation() = default;
+  ErrorODRViolation(u32 tid, const __asan_global *g1, u32 stack_id1_,
+                    const __asan_global *g2, u32 stack_id2_)
+      : ErrorBase(tid),
+        global1(*g1),
+        global2(*g2),
+        stack_id1(stack_id1_),
+        stack_id2(stack_id2_) {}
+  void Print();
+};
+
+struct ErrorInvalidPointerPair : ErrorBase {
+  uptr pc, bp, sp;
+  AddressDescription addr1_description;
+  AddressDescription addr2_description;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorInvalidPointerPair() = default;
+  ErrorInvalidPointerPair(u32 tid, uptr pc_, uptr bp_, uptr sp_, uptr p1,
+                          uptr p2)
+      : ErrorBase(tid),
+        pc(pc_),
+        bp(bp_),
+        sp(sp_),
+        addr1_description(p1, 1, /*shouldLockThreadRegistry=*/false),
+        addr2_description(p2, 1, /*shouldLockThreadRegistry=*/false) {}
+  void Print();
+};
+
+struct ErrorGeneric : ErrorBase {
+  AddressDescription addr_description;
+  uptr pc, bp, sp;
+  uptr access_size;
+  const char *bug_descr;
+  bool is_write;
+  u8 shadow_val;
+  // VS2013 doesn't implement unrestricted unions, so we need a trivial default
+  // constructor
+  ErrorGeneric() = default;
+  ErrorGeneric(u32 tid, uptr addr, uptr pc_, uptr bp_, uptr sp_, bool is_write_,
+               uptr access_size_);
+  void Print();
+};
+
+// clang-format off
+#define ASAN_FOR_EACH_ERROR_KIND(macro)         \
+  macro(StackOverflow)                          \
+  macro(DeadlySignal)                           \
+  macro(DoubleFree)                             \
+  macro(NewDeleteSizeMismatch)                  \
+  macro(FreeNotMalloced)                        \
+  macro(AllocTypeMismatch)                      \
+  macro(MallocUsableSizeNotOwned)               \
+  macro(SanitizerGetAllocatedSizeNotOwned)      \
+  macro(StringFunctionMemoryRangesOverlap)      \
+  macro(StringFunctionSizeOverflow)             \
+  macro(BadParamsToAnnotateContiguousContainer) \
+  macro(ODRViolation)                           \
+  macro(InvalidPointerPair)                     \
+  macro(Generic)
+// clang-format on
+
+#define ASAN_DEFINE_ERROR_KIND(name) kErrorKind##name,
+#define ASAN_ERROR_DESCRIPTION_MEMBER(name) Error##name name;
+#define ASAN_ERROR_DESCRIPTION_CONSTRUCTOR(name) \
+  ErrorDescription(Error##name const &e) : kind(kErrorKind##name), name(e) {}
+#define ASAN_ERROR_DESCRIPTION_PRINT(name) \
+  case kErrorKind##name:                   \
+    return name.Print();
+
+enum ErrorKind {
+  kErrorKindInvalid = 0,
+  ASAN_FOR_EACH_ERROR_KIND(ASAN_DEFINE_ERROR_KIND)
+};
+
+struct ErrorDescription {
+  ErrorKind kind;
+  // We're using a tagged union because it allows us to have a trivially
+  // copiable type and use the same structures as the public interface.
+  //
+  // We can add a wrapper around it to make it "more c++-like", but that would
+  // add a lot of code and the benefit wouldn't be that big.
+  union {
+    ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_MEMBER)
+  };
+
+  ErrorDescription() { internal_memset(this, 0, sizeof(*this)); }
+  ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_CONSTRUCTOR)
+
+  bool IsValid() { return kind != kErrorKindInvalid; }
+  void Print() {
+    switch (kind) {
+      ASAN_FOR_EACH_ERROR_KIND(ASAN_ERROR_DESCRIPTION_PRINT)
+      case kErrorKindInvalid:
+        CHECK(0);
+    }
+    CHECK(0);
+  }
+};
+
+#undef ASAN_FOR_EACH_ERROR_KIND
+#undef ASAN_DEFINE_ERROR_KIND
+#undef ASAN_ERROR_DESCRIPTION_MEMBER
+#undef ASAN_ERROR_DESCRIPTION_CONSTRUCTOR
+#undef ASAN_ERROR_DESCRIPTION_PRINT
+
+}  // namespace __asan
+
+#endif  // ASAN_ERRORS_H
diff --git a/lib/asan/asan_globals.cc b/lib/asan/asan_globals.cc
index f185761..93dae29 100644
--- a/lib/asan/asan_globals.cc
+++ b/lib/asan/asan_globals.cc
@@ -25,6 +25,7 @@
 #include "sanitizer_common/sanitizer_mutex.h"
 #include "sanitizer_common/sanitizer_placement_new.h"
 #include "sanitizer_common/sanitizer_stackdepot.h"
+#include "sanitizer_common/sanitizer_symbolizer.h"
 
 namespace __asan {
 
@@ -123,18 +124,6 @@
   return res;
 }
 
-bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr) {
-  Global g = {};
-  if (GetGlobalsForAddress(addr, &g, nullptr, 1)) {
-    internal_strncpy(descr->name, g.name, descr->name_size);
-    descr->region_address = g.beg;
-    descr->region_size = g.size;
-    descr->region_kind = "global";
-    return true;
-  }
-  return false;
-}
-
 enum GlobalSymbolState {
   UNREGISTERED = 0,
   REGISTERED = 1
@@ -279,6 +268,46 @@
   }
 }
 
+static bool IsASCII(unsigned char c) { return /*0x00 <= c &&*/ c <= 0x7F; }
+
+const char *MaybeDemangleGlobalName(const char *name) {
+  // We can spoil names of globals with C linkage, so use an heuristic
+  // approach to check if the name should be demangled.
+  bool should_demangle = false;
+  if (name[0] == '_' && name[1] == 'Z')
+    should_demangle = true;
+  else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
+    should_demangle = true;
+
+  return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
+}
+
+// Check if the global is a zero-terminated ASCII string. If so, print it.
+void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g) {
+  for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
+    unsigned char c = *(unsigned char *)p;
+    if (c == '\0' || !IsASCII(c)) return;
+  }
+  if (*(char *)(g.beg + g.size - 1) != '\0') return;
+  str->append("  '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
+              (char *)g.beg);
+}
+
+static const char *GlobalFilename(const __asan_global &g) {
+  const char *res = g.module_name;
+  // Prefer the filename from source location, if is available.
+  if (g.location) res = g.location->filename;
+  CHECK(res);
+  return res;
+}
+
+void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g) {
+  str->append("%s", GlobalFilename(g));
+  if (!g.location) return;
+  if (g.location->line_no) str->append(":%d", g.location->line_no);
+  if (g.location->column_no) str->append(":%d", g.location->column_no);
+}
+
 } // namespace __asan
 
 // ---------------------- Interface ---------------- {{{1
diff --git a/lib/asan/asan_internal.h b/lib/asan/asan_internal.h
index 2014237..84d7f08 100644
--- a/lib/asan/asan_internal.h
+++ b/lib/asan/asan_internal.h
@@ -65,6 +65,9 @@
 // asan_win.cc
 void InitializePlatformExceptionHandlers();
 
+// asan_win.cc / asan_posix.cc
+const char *DescribeSignalOrException(int signo);
+
 // asan_rtl.cc
 void NORETURN ShowStatsAndAbort();
 
@@ -132,12 +135,10 @@
 extern void (*death_callback)(void);
 // These magic values are written to shadow for better error reporting.
 const int kAsanHeapLeftRedzoneMagic = 0xfa;
-const int kAsanHeapRightRedzoneMagic = 0xfb;
 const int kAsanHeapFreeMagic = 0xfd;
 const int kAsanStackLeftRedzoneMagic = 0xf1;
 const int kAsanStackMidRedzoneMagic = 0xf2;
 const int kAsanStackRightRedzoneMagic = 0xf3;
-const int kAsanStackPartialRedzoneMagic = 0xf4;
 const int kAsanStackAfterReturnMagic = 0xf5;
 const int kAsanInitializationOrderMagic = 0xf6;
 const int kAsanUserPoisonedMemoryMagic = 0xf7;
diff --git a/lib/asan/asan_posix.cc b/lib/asan/asan_posix.cc
index 84a29ec..8e56763 100644
--- a/lib/asan/asan_posix.cc
+++ b/lib/asan/asan_posix.cc
@@ -33,6 +33,19 @@
 
 namespace __asan {
 
+const char *DescribeSignalOrException(int signo) {
+  switch (signo) {
+    case SIGFPE:
+      return "FPE";
+    case SIGILL:
+      return "ILL";
+    case SIGABRT:
+      return "ABRT";
+    default:
+      return "SEGV";
+  }
+}
+
 void AsanOnDeadlySignal(int signo, void *siginfo, void *context) {
   ScopedDeadlySignal signal_scope(GetCurrentThread());
   int code = (int)((siginfo_t*)siginfo)->si_code;
@@ -84,12 +97,8 @@
   // unaligned memory access.
   if (IsStackAccess && (code == si_SEGV_MAPERR || code == si_SEGV_ACCERR))
     ReportStackOverflow(sig);
-  else if (signo == SIGFPE)
-    ReportDeadlySignal("FPE", sig);
-  else if (signo == SIGILL)
-    ReportDeadlySignal("ILL", sig);
   else
-    ReportDeadlySignal("SEGV", sig);
+    ReportDeadlySignal(signo, sig);
 }
 
 // ---------------------- TSD ---------------- {{{1
diff --git a/lib/asan/asan_report.cc b/lib/asan/asan_report.cc
index 220eba3..9748634 100644
--- a/lib/asan/asan_report.cc
+++ b/lib/asan/asan_report.cc
@@ -12,6 +12,7 @@
 // This file contains error reporting code.
 //===----------------------------------------------------------------------===//
 
+#include "asan_errors.h"
 #include "asan_flags.h"
 #include "asan_descriptions.h"
 #include "asan_internal.h"
@@ -36,19 +37,6 @@
 static const unsigned kAsanBuggyPcPoolSize = 25;
 static __sanitizer::atomic_uintptr_t AsanBuggyPcPool[kAsanBuggyPcPoolSize];
 
-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) {
   BlockingMutexLock l(&error_message_buf_mutex);
   if (!error_message_buffer) {
@@ -68,8 +56,8 @@
 
 // ---------------------- Helper functions ----------------------- {{{1
 
-static void PrintMemoryByte(InternalScopedString *str, const char *before,
-    u8 byte, bool in_shadow, const char *after = "\n") {
+void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
+                     bool in_shadow, const char *after) {
   Decorator d;
   str->append("%s%s%x%x%s%s", before,
               in_shadow ? d.ShadowByte(byte) : d.MemoryByte(),
@@ -77,99 +65,6 @@
               in_shadow ? d.EndShadowByte() : d.EndMemoryByte(), after);
 }
 
-static void PrintShadowByte(InternalScopedString *str, const char *before,
-    u8 byte, const char *after = "\n") {
-  PrintMemoryByte(str, before, byte, /*in_shadow*/true, after);
-}
-
-static void PrintShadowBytes(InternalScopedString *str, const char *before,
-                             u8 *bytes, u8 *guilty, uptr n) {
-  Decorator d;
-  if (before) str->append("%s%p:", before, bytes);
-  for (uptr i = 0; i < n; i++) {
-    u8 *p = bytes + i;
-    const char *before =
-        p == guilty ? "[" : (p - 1 == guilty && i != 0) ? "" : " ";
-    const char *after = p == guilty ? "]" : "";
-    PrintShadowByte(str, before, *p, after);
-  }
-  str->append("\n");
-}
-
-static void PrintLegend(InternalScopedString *str) {
-  str->append(
-      "Shadow byte legend (one shadow byte represents %d "
-      "application bytes):\n",
-      (int)SHADOW_GRANULARITY);
-  PrintShadowByte(str, "  Addressable:           ", 0);
-  str->append("  Partially addressable: ");
-  for (u8 i = 1; i < SHADOW_GRANULARITY; i++) PrintShadowByte(str, "", i, " ");
-  str->append("\n");
-  PrintShadowByte(str, "  Heap left redzone:       ",
-                  kAsanHeapLeftRedzoneMagic);
-  PrintShadowByte(str, "  Heap right redzone:      ",
-                  kAsanHeapRightRedzoneMagic);
-  PrintShadowByte(str, "  Freed heap region:       ", kAsanHeapFreeMagic);
-  PrintShadowByte(str, "  Stack left redzone:      ",
-                  kAsanStackLeftRedzoneMagic);
-  PrintShadowByte(str, "  Stack mid redzone:       ",
-                  kAsanStackMidRedzoneMagic);
-  PrintShadowByte(str, "  Stack right redzone:     ",
-                  kAsanStackRightRedzoneMagic);
-  PrintShadowByte(str, "  Stack partial redzone:   ",
-                  kAsanStackPartialRedzoneMagic);
-  PrintShadowByte(str, "  Stack after return:      ",
-                  kAsanStackAfterReturnMagic);
-  PrintShadowByte(str, "  Stack use after scope:   ",
-                  kAsanStackUseAfterScopeMagic);
-  PrintShadowByte(str, "  Global redzone:          ", kAsanGlobalRedzoneMagic);
-  PrintShadowByte(str, "  Global init order:       ",
-                  kAsanInitializationOrderMagic);
-  PrintShadowByte(str, "  Poisoned by user:        ",
-                  kAsanUserPoisonedMemoryMagic);
-  PrintShadowByte(str, "  Container overflow:      ",
-                  kAsanContiguousContainerOOBMagic);
-  PrintShadowByte(str, "  Array cookie:            ",
-                  kAsanArrayCookieMagic);
-  PrintShadowByte(str, "  Intra object redzone:    ",
-                  kAsanIntraObjectRedzone);
-  PrintShadowByte(str, "  ASan internal:           ", kAsanInternalHeapMagic);
-  PrintShadowByte(str, "  Left alloca redzone:     ", kAsanAllocaLeftMagic);
-  PrintShadowByte(str, "  Right alloca redzone:    ", kAsanAllocaRightMagic);
-}
-
-void MaybeDumpInstructionBytes(uptr pc) {
-  if (!flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
-    return;
-  InternalScopedString str(1024);
-  str.append("First 16 instruction bytes at pc: ");
-  if (IsAccessibleMemoryRange(pc, 16)) {
-    for (int i = 0; i < 16; ++i) {
-      PrintMemoryByte(&str, "", ((u8 *)pc)[i], /*in_shadow*/false, " ");
-    }
-    str.append("\n");
-  } else {
-    str.append("unaccessible\n");
-  }
-  Report("%s", str.data());
-}
-
-static void PrintShadowMemoryForAddress(uptr addr) {
-  if (!AddrIsInMem(addr)) return;
-  uptr shadow_addr = MemToShadow(addr);
-  const uptr n_bytes_per_row = 16;
-  uptr aligned_shadow = shadow_addr & ~(n_bytes_per_row - 1);
-  InternalScopedString str(4096 * 8);
-  str.append("Shadow bytes around the buggy address:\n");
-  for (int i = -5; i <= 5; i++) {
-    const char *prefix = (i == 0) ? "=>" : "  ";
-    PrintShadowBytes(&str, prefix, (u8 *)(aligned_shadow + i * n_bytes_per_row),
-                     (u8 *)shadow_addr, n_bytes_per_row);
-  }
-  if (flags()->print_legend) PrintLegend(&str);
-  Printf("%s", str.data());
-}
-
 static void PrintZoneForPointer(uptr ptr, uptr zone_ptr,
                                 const char *zone_name) {
   if (zone_ptr) {
@@ -187,144 +82,6 @@
 
 // ---------------------- Address Descriptions ------------------- {{{1
 
-static bool IsASCII(unsigned char c) {
-  return /*0x00 <= c &&*/ c <= 0x7F;
-}
-
-static const char *MaybeDemangleGlobalName(const char *name) {
-  // We can spoil names of globals with C linkage, so use an heuristic
-  // approach to check if the name should be demangled.
-  bool should_demangle = false;
-  if (name[0] == '_' && name[1] == 'Z')
-    should_demangle = true;
-  else if (SANITIZER_WINDOWS && name[0] == '\01' && name[1] == '?')
-    should_demangle = true;
-
-  return should_demangle ? Symbolizer::GetOrInit()->Demangle(name) : name;
-}
-
-// Check if the global is a zero-terminated ASCII string. If so, print it.
-static void PrintGlobalNameIfASCII(InternalScopedString *str,
-                                   const __asan_global &g) {
-  for (uptr p = g.beg; p < g.beg + g.size - 1; p++) {
-    unsigned char c = *(unsigned char*)p;
-    if (c == '\0' || !IsASCII(c)) return;
-  }
-  if (*(char*)(g.beg + g.size - 1) != '\0') return;
-  str->append("  '%s' is ascii string '%s'\n", MaybeDemangleGlobalName(g.name),
-              (char *)g.beg);
-}
-
-static const char *GlobalFilename(const __asan_global &g) {
-  const char *res = g.module_name;
-  // Prefer the filename from source location, if is available.
-  if (g.location)
-    res = g.location->filename;
-  CHECK(res);
-  return res;
-}
-
-static void PrintGlobalLocation(InternalScopedString *str,
-                                const __asan_global &g) {
-  str->append("%s", GlobalFilename(g));
-  if (!g.location)
-    return;
-  if (g.location->line_no)
-    str->append(":%d", g.location->line_no);
-  if (g.location->column_no)
-    str->append(":%d", g.location->column_no);
-}
-
-static void DescribeAddressRelativeToGlobal(uptr addr, uptr size,
-                                            const __asan_global &g) {
-  InternalScopedString str(4096);
-  Decorator d;
-  str.append("%s", d.Location());
-  if (addr < g.beg) {
-    str.append("%p is located %zd bytes to the left", (void *)addr,
-               g.beg - addr);
-  } else if (addr + size > g.beg + g.size) {
-    if (addr < g.beg + g.size)
-      addr = g.beg + g.size;
-    str.append("%p is located %zd bytes to the right", (void *)addr,
-               addr - (g.beg + g.size));
-  } else {
-    // Can it happen?
-    str.append("%p is located %zd bytes inside", (void *)addr, addr - g.beg);
-  }
-  str.append(" of global variable '%s' defined in '",
-             MaybeDemangleGlobalName(g.name));
-  PrintGlobalLocation(&str, g);
-  str.append("' (0x%zx) of size %zu\n", g.beg, g.size);
-  str.append("%s", d.EndLocation());
-  PrintGlobalNameIfASCII(&str, g);
-  Printf("%s", str.data());
-}
-
-static bool DescribeAddressIfGlobal(uptr addr, uptr size,
-                                    const char *bug_type) {
-  // Assume address is close to at most four globals.
-  const int kMaxGlobalsInReport = 4;
-  __asan_global globals[kMaxGlobalsInReport];
-  u32 reg_sites[kMaxGlobalsInReport];
-  int globals_num =
-      GetGlobalsForAddress(addr, globals, reg_sites, ARRAY_SIZE(globals));
-  if (globals_num == 0)
-    return false;
-  for (int i = 0; i < globals_num; i++) {
-    DescribeAddressRelativeToGlobal(addr, size, globals[i]);
-    if (0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&
-        reg_sites[i]) {
-      Printf("  registered at:\n");
-      StackDepotGet(reg_sites[i]).Print();
-    }
-  }
-  return true;
-}
-
-static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,
-                                          uptr access_size, uptr prev_var_end,
-                                          uptr next_var_beg) {
-  uptr var_end = var.beg + var.size;
-  uptr addr_end = addr + access_size;
-  const char *pos_descr = nullptr;
-  // If the variable [var.beg, var_end) is the nearest variable to the
-  // current memory access, indicate it in the log.
-  if (addr >= var.beg) {
-    if (addr_end <= var_end)
-      pos_descr = "is inside";  // May happen if this is a use-after-return.
-    else if (addr < var_end)
-      pos_descr = "partially overflows";
-    else if (addr_end <= next_var_beg &&
-             next_var_beg - addr_end >= addr - var_end)
-      pos_descr = "overflows";
-  } else {
-    if (addr_end > var.beg)
-      pos_descr = "partially underflows";
-    else if (addr >= prev_var_end &&
-             addr - prev_var_end >= var.beg - addr_end)
-      pos_descr = "underflows";
-  }
-  InternalScopedString str(1024);
-  str.append("    [%zd, %zd)", var.beg, var_end);
-  // Render variable name.
-  str.append(" '");
-  for (uptr i = 0; i < var.name_len; ++i) {
-    str.append("%c", var.name_pos[i]);
-  }
-  str.append("'");
-  if (pos_descr) {
-    Decorator d;
-    // FIXME: we may want to also print the size of the access here,
-    // but in case of accesses generated by memset it may be confusing.
-    str.append("%s <== Memory access at offset %zd %s this variable%s\n",
-               d.Location(), addr, pos_descr, d.EndLocation());
-  } else {
-    str.append("\n");
-  }
-  Printf("%s", str.data());
-}
-
 bool ParseFrameDescription(const char *frame_descr,
                            InternalMmapVector<StackVarDescr> *vars) {
   CHECK(frame_descr);
@@ -352,163 +109,17 @@
   return true;
 }
 
-bool DescribeAddressIfStack(uptr addr, uptr access_size) {
-  AsanThread *t = FindThreadByStackAddress(addr);
-  if (!t) return false;
-
-  Decorator d;
-  char tname[128];
-  Printf("%s", d.Location());
-  Printf("Address %p is located in stack of thread T%d%s", addr, t->tid(),
-         ThreadNameWithParenthesis(t->tid(), tname, sizeof(tname)));
-
-  // Try to fetch precise stack frame for this access.
-  AsanThread::StackFrameAccess access;
-  if (!t->GetStackFrameAccessByAddr(addr, &access)) {
-    Printf("%s\n", d.EndLocation());
-    return true;
-  }
-  Printf(" at offset %zu in frame%s\n", access.offset, d.EndLocation());
-
-  // Now we print the frame where the alloca has happened.
-  // We print this frame as a stack trace with one element.
-  // The symbolizer may print more than one frame if inlining was involved.
-  // The frame numbers may be different than those in the stack trace printed
-  // previously. That's unfortunate, but I have no better solution,
-  // especially given that the alloca may be from entirely different place
-  // (e.g. use-after-scope, or different thread's stack).
-#if SANITIZER_PPC64V1
-  // On PowerPC64 ELFv1, the address of a function actually points to a
-  // three-doubleword data structure with the first field containing
-  // the address of the function's code.
-  access.frame_pc = *reinterpret_cast<uptr *>(access.frame_pc);
-#endif
-  access.frame_pc += 16;
-  Printf("%s", d.EndLocation());
-  StackTrace alloca_stack(&access.frame_pc, 1);
-  alloca_stack.Print();
-
-  InternalMmapVector<StackVarDescr> vars(16);
-  if (!ParseFrameDescription(access.frame_descr, &vars)) {
-    Printf("AddressSanitizer can't parse the stack frame "
-           "descriptor: |%s|\n", access.frame_descr);
-    // 'addr' is a stack address, so return true even if we can't parse frame
-    return true;
-  }
-  uptr n_objects = vars.size();
-  // Report the number of stack objects.
-  Printf("  This frame has %zu object(s):\n", n_objects);
-
-  // Report all objects in this frame.
-  for (uptr i = 0; i < n_objects; i++) {
-    uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;
-    uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);
-    PrintAccessAndVarIntersection(vars[i], access.offset, access_size,
-                                  prev_var_end, next_var_beg);
-  }
-  Printf("HINT: this may be a false positive if your program uses "
-         "some custom stack unwind mechanism or swapcontext\n");
-  if (SANITIZER_WINDOWS)
-    Printf("      (longjmp, SEH and C++ exceptions *are* supported)\n");
-  else
-    Printf("      (longjmp and C++ exceptions *are* supported)\n");
-
-  DescribeThread(t);
-  return true;
-}
-
-static void DescribeAccessToHeapChunk(AsanChunkView chunk, uptr addr,
-                                      uptr access_size) {
-  sptr offset;
-  Decorator d;
-  InternalScopedString str(4096);
-  str.append("%s", d.Location());
-  if (chunk.AddrIsAtLeft(addr, access_size, &offset)) {
-    str.append("%p is located %zd bytes to the left of", (void *)addr, offset);
-  } else if (chunk.AddrIsAtRight(addr, access_size, &offset)) {
-    if (offset < 0) {
-      addr -= offset;
-      offset = 0;
-    }
-    str.append("%p is located %zd bytes to the right of", (void *)addr, offset);
-  } else if (chunk.AddrIsInside(addr, access_size, &offset)) {
-    str.append("%p is located %zd bytes inside of", (void*)addr, offset);
-  } else {
-    str.append("%p is located somewhere around (this is AddressSanitizer bug!)",
-               (void *)addr);
-  }
-  str.append(" %zu-byte region [%p,%p)\n", chunk.UsedSize(),
-             (void *)(chunk.Beg()), (void *)(chunk.End()));
-  str.append("%s", d.EndLocation());
-  Printf("%s", str.data());
-}
-
-void DescribeHeapAddress(uptr addr, uptr access_size) {
-  AsanChunkView chunk = FindHeapChunkByAddress(addr);
-  if (!chunk.IsValid()) {
-    Printf("AddressSanitizer can not describe address in more detail "
-           "(wild memory access suspected).\n");
-    return;
-  }
-  DescribeAccessToHeapChunk(chunk, addr, access_size);
-  CHECK_NE(chunk.AllocTid(), kInvalidTid);
-  asanThreadRegistry().CheckLocked();
-  AsanThreadContext *alloc_thread =
-      GetThreadContextByTidLocked(chunk.AllocTid());
-  StackTrace alloc_stack = chunk.GetAllocStack();
-  char tname[128];
-  Decorator d;
-  AsanThreadContext *free_thread = nullptr;
-  if (chunk.FreeTid() != kInvalidTid) {
-    free_thread = GetThreadContextByTidLocked(chunk.FreeTid());
-    Printf("%sfreed by thread T%d%s here:%s\n", d.Allocation(),
-           free_thread->tid,
-           ThreadNameWithParenthesis(free_thread, tname, sizeof(tname)),
-           d.EndAllocation());
-    StackTrace free_stack = chunk.GetFreeStack();
-    free_stack.Print();
-    Printf("%spreviously allocated by thread T%d%s here:%s\n",
-           d.Allocation(), alloc_thread->tid,
-           ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
-           d.EndAllocation());
-  } else {
-    Printf("%sallocated by thread T%d%s here:%s\n", d.Allocation(),
-           alloc_thread->tid,
-           ThreadNameWithParenthesis(alloc_thread, tname, sizeof(tname)),
-           d.EndAllocation());
-  }
-  alloc_stack.Print();
-  DescribeThread(GetCurrentThread());
-  if (free_thread)
-    DescribeThread(free_thread);
-  DescribeThread(alloc_thread);
-}
-
-static void DescribeAddress(uptr addr, uptr access_size, const char *bug_type) {
-  // Check if this is shadow or shadow gap.
-  if (DescribeAddressIfShadow(addr))
-    return;
-  CHECK(AddrIsInMem(addr));
-  if (DescribeAddressIfGlobal(addr, access_size, bug_type))
-    return;
-  if (DescribeAddressIfStack(addr, access_size))
-    return;
-  // Assume it is a heap address.
-  DescribeHeapAddress(addr, access_size);
-}
-
 // -------------------- Different kinds of reports ----------------- {{{1
 
 // Use ScopedInErrorReport to run common actions just before and
 // immediately after printing error report.
 class ScopedInErrorReport {
  public:
-  explicit ScopedInErrorReport(ReportData *report = nullptr,
-                               bool fatal = false) {
+  explicit ScopedInErrorReport(bool fatal = false) {
     halt_on_error_ = fatal || flags()->halt_on_error;
 
     if (lock_.TryLock()) {
-      StartReporting(report);
+      StartReporting();
       return;
     }
 
@@ -550,10 +161,13 @@
       lock_.Lock();
     }
 
-    StartReporting(report);
+    StartReporting();
   }
 
   ~ScopedInErrorReport() {
+    ASAN_ON_ERROR();
+    if (current_error_.IsValid()) current_error_.Print();
+
     // Make sure the current thread is announced.
     DescribeThread(GetCurrentThread());
     // We may want to grab this lock again when printing stats.
@@ -579,6 +193,12 @@
     if (error_report_callback) {
       error_report_callback(buffer_copy.data());
     }
+
+    // In halt_on_error = false mode, reset the current error object (before
+    // unlocking).
+    if (!halt_on_error_)
+      internal_memset(&current_error_, 0, sizeof(current_error_));
+
     CommonSanitizerReportMutex.Unlock();
     reporting_thread_tid_ = kInvalidTid;
     lock_.Unlock();
@@ -588,11 +208,18 @@
     }
   }
 
+  void ReportError(const ErrorDescription &description) {
+    // Can only report one error per ScopedInErrorReport.
+    CHECK_EQ(current_error_.kind, kErrorKindInvalid);
+    current_error_ = description;
+  }
+
+  static ErrorDescription &CurrentError() {
+    return current_error_;
+  }
+
  private:
-  void StartReporting(ReportData *report) {
-    if (report) report_data = *report;
-    report_happened = true;
-    ASAN_ON_ERROR();
+  void StartReporting() {
     // Make sure the registry and sanitizer report mutexes are locked while
     // we're printing an error report.
     // We can lock them only here to avoid self-deadlock in case of
@@ -606,181 +233,69 @@
 
   static StaticSpinMutex lock_;
   static u32 reporting_thread_tid_;
+  // Error currently being reported. This enables the destructor to interact
+  // with the debugger and point it to an error description.
+  static ErrorDescription current_error_;
   bool halt_on_error_;
 };
 
 StaticSpinMutex ScopedInErrorReport::lock_;
 u32 ScopedInErrorReport::reporting_thread_tid_ = kInvalidTid;
+ErrorDescription ScopedInErrorReport::current_error_;
 
 void ReportStackOverflow(const SignalContext &sig) {
-  ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true);
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report(
-      "ERROR: AddressSanitizer: stack-overflow on address %p"
-      " (pc %p bp %p sp %p T%d)\n",
-      (void *)sig.addr, (void *)sig.pc, (void *)sig.bp, (void *)sig.sp,
-      GetCurrentTidOrInvalid());
-  Printf("%s", d.EndWarning());
-  ScarinessScore::PrintSimple(10, "stack-overflow");
-  GET_STACK_TRACE_SIGNAL(sig);
-  stack.Print();
-  ReportErrorSummary("stack-overflow", &stack);
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorStackOverflow error(GetCurrentTidOrInvalid(), sig);
+  in_report.ReportError(error);
 }
 
-void ReportDeadlySignal(const char *description, const SignalContext &sig) {
-  ScopedInErrorReport in_report(/*report*/ nullptr, /*fatal*/ true);
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report(
-      "ERROR: AddressSanitizer: %s on unknown address %p"
-      " (pc %p bp %p sp %p T%d)\n",
-      description, (void *)sig.addr, (void *)sig.pc, (void *)sig.bp,
-      (void *)sig.sp, GetCurrentTidOrInvalid());
-  Printf("%s", d.EndWarning());
-  ScarinessScore SS;
-  if (sig.pc < GetPageSizeCached())
-    Report("Hint: pc points to the zero page.\n");
-  if (sig.is_memory_access) {
-    const char *access_type =
-        sig.write_flag == SignalContext::WRITE
-            ? "WRITE"
-            : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN");
-    Report("The signal is caused by a %s memory access.\n", access_type);
-    if (sig.addr < GetPageSizeCached()) {
-      Report("Hint: address points to the zero page.\n");
-      SS.Scare(10, "null-deref");
-    } else if (sig.addr == sig.pc) {
-      SS.Scare(60, "wild-jump");
-    } else if (sig.write_flag == SignalContext::WRITE) {
-      SS.Scare(30, "wild-addr-write");
-    } else if (sig.write_flag == SignalContext::READ) {
-      SS.Scare(20, "wild-addr-read");
-    } else {
-      SS.Scare(25, "wild-addr");
-    }
-  } else {
-    SS.Scare(10, "signal");
-  }
-  SS.Print();
-  GET_STACK_TRACE_SIGNAL(sig);
-  stack.Print();
-  MaybeDumpInstructionBytes(sig.pc);
-  Printf("AddressSanitizer can not provide additional info.\n");
-  ReportErrorSummary(description, &stack);
+void ReportDeadlySignal(int signo, const SignalContext &sig) {
+  ScopedInErrorReport in_report(/*fatal*/ true);
+  ErrorDeadlySignal error(GetCurrentTidOrInvalid(), sig, signo);
+  in_report.ReportError(error);
 }
 
 void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  char tname[128];
-  u32 curr_tid = GetCurrentTidOrInvalid();
-  Report("ERROR: AddressSanitizer: attempting double-free on %p in "
-         "thread T%d%s:\n",
-         addr, curr_tid,
-         ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
-  Printf("%s", d.EndWarning());
-  CHECK_GT(free_stack->size, 0);
-  ScarinessScore::PrintSimple(42, "double-free");
-  GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
-  stack.Print();
-  DescribeHeapAddress(addr, 1);
-  ReportErrorSummary("double-free", &stack);
+  ErrorDoubleFree error(GetCurrentTidOrInvalid(), free_stack, addr);
+  in_report.ReportError(error);
 }
 
-void ReportNewDeleteSizeMismatch(uptr addr, uptr alloc_size, uptr delete_size,
+void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
                                  BufferedStackTrace *free_stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  char tname[128];
-  u32 curr_tid = GetCurrentTidOrInvalid();
-  Report("ERROR: AddressSanitizer: new-delete-type-mismatch on %p in "
-         "thread T%d%s:\n",
-         addr, curr_tid,
-         ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
-  Printf("%s  object passed to delete has wrong type:\n", d.EndWarning());
-  Printf("  size of the allocated type:   %zd bytes;\n"
-         "  size of the deallocated type: %zd bytes.\n",
-         alloc_size, delete_size);
-  CHECK_GT(free_stack->size, 0);
-  ScarinessScore::PrintSimple(10, "new-delete-type-mismatch");
-  GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
-  stack.Print();
-  DescribeHeapAddress(addr, 1);
-  ReportErrorSummary("new-delete-type-mismatch", &stack);
-  Report("HINT: if you don't care about these errors you may set "
-         "ASAN_OPTIONS=new_delete_type_mismatch=0\n");
+  ErrorNewDeleteSizeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
+                                   delete_size);
+  in_report.ReportError(error);
 }
 
 void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  char tname[128];
-  u32 curr_tid = GetCurrentTidOrInvalid();
-  Report("ERROR: AddressSanitizer: attempting free on address "
-             "which was not malloc()-ed: %p in thread T%d%s\n", addr,
-         curr_tid, ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)));
-  Printf("%s", d.EndWarning());
-  CHECK_GT(free_stack->size, 0);
-  ScarinessScore::PrintSimple(40, "bad-free");
-  GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
-  stack.Print();
-  DescribeHeapAddress(addr, 1);
-  ReportErrorSummary("bad-free", &stack);
+  ErrorFreeNotMalloced error(GetCurrentTidOrInvalid(), free_stack, addr);
+  in_report.ReportError(error);
 }
 
 void ReportAllocTypeMismatch(uptr addr, BufferedStackTrace *free_stack,
                              AllocType alloc_type,
                              AllocType dealloc_type) {
-  static const char *alloc_names[] =
-    {"INVALID", "malloc", "operator new", "operator new []"};
-  static const char *dealloc_names[] =
-    {"INVALID", "free", "operator delete", "operator delete []"};
-  CHECK_NE(alloc_type, dealloc_type);
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: alloc-dealloc-mismatch (%s vs %s) on %p\n",
-        alloc_names[alloc_type], dealloc_names[dealloc_type], addr);
-  Printf("%s", d.EndWarning());
-  CHECK_GT(free_stack->size, 0);
-  ScarinessScore::PrintSimple(10, "alloc-dealloc-mismatch");
-  GET_STACK_TRACE_FATAL(free_stack->trace[0], free_stack->top_frame_bp);
-  stack.Print();
-  DescribeHeapAddress(addr, 1);
-  ReportErrorSummary("alloc-dealloc-mismatch", &stack);
-  Report("HINT: if you don't care about these errors you may set "
-         "ASAN_OPTIONS=alloc_dealloc_mismatch=0\n");
+  ErrorAllocTypeMismatch error(GetCurrentTidOrInvalid(), free_stack, addr,
+                               alloc_type, dealloc_type);
+  in_report.ReportError(error);
 }
 
 void ReportMallocUsableSizeNotOwned(uptr addr, BufferedStackTrace *stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: attempting to call "
-             "malloc_usable_size() for pointer which is "
-             "not owned: %p\n", addr);
-  Printf("%s", d.EndWarning());
-  stack->Print();
-  DescribeHeapAddress(addr, 1);
-  ReportErrorSummary("bad-malloc_usable_size", stack);
+  ErrorMallocUsableSizeNotOwned error(GetCurrentTidOrInvalid(), stack, addr);
+  in_report.ReportError(error);
 }
 
 void ReportSanitizerGetAllocatedSizeNotOwned(uptr addr,
                                              BufferedStackTrace *stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: attempting to call "
-             "__sanitizer_get_allocated_size() for pointer which is "
-             "not owned: %p\n", addr);
-  Printf("%s", d.EndWarning());
-  stack->Print();
-  DescribeHeapAddress(addr, 1);
-  ReportErrorSummary("bad-__sanitizer_get_allocated_size", stack);
+  ErrorSanitizerGetAllocatedSizeNotOwned error(GetCurrentTidOrInvalid(), stack,
+                                               addr);
+  in_report.ReportError(error);
 }
 
 void ReportStringFunctionMemoryRangesOverlap(const char *function,
@@ -788,96 +303,43 @@
                                              const char *offset2, uptr length2,
                                              BufferedStackTrace *stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  char bug_type[100];
-  internal_snprintf(bug_type, sizeof(bug_type), "%s-param-overlap", function);
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: %s: "
-             "memory ranges [%p,%p) and [%p, %p) overlap\n", \
-             bug_type, offset1, offset1 + length1, offset2, offset2 + length2);
-  Printf("%s", d.EndWarning());
-  ScarinessScore::PrintSimple(10, bug_type);
-  stack->Print();
-  DescribeAddress((uptr)offset1, length1, bug_type);
-  DescribeAddress((uptr)offset2, length2, bug_type);
-  ReportErrorSummary(bug_type, stack);
+  ErrorStringFunctionMemoryRangesOverlap error(
+      GetCurrentTidOrInvalid(), stack, (uptr)offset1, length1, (uptr)offset2,
+      length2, function);
+  in_report.ReportError(error);
 }
 
 void ReportStringFunctionSizeOverflow(uptr offset, uptr size,
                                       BufferedStackTrace *stack) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  const char *bug_type = "negative-size-param";
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: %s: (size=%zd)\n", bug_type, size);
-  Printf("%s", d.EndWarning());
-  ScarinessScore::PrintSimple(10, bug_type);
-  stack->Print();
-  DescribeAddress(offset, size, bug_type);
-  ReportErrorSummary(bug_type, stack);
+  ErrorStringFunctionSizeOverflow error(GetCurrentTidOrInvalid(), stack, offset,
+                                        size);
+  in_report.ReportError(error);
 }
 
 void ReportBadParamsToAnnotateContiguousContainer(uptr beg, uptr end,
                                                   uptr old_mid, uptr new_mid,
                                                   BufferedStackTrace *stack) {
   ScopedInErrorReport in_report;
-  Report("ERROR: AddressSanitizer: bad parameters to "
-         "__sanitizer_annotate_contiguous_container:\n"
-         "      beg     : %p\n"
-         "      end     : %p\n"
-         "      old_mid : %p\n"
-         "      new_mid : %p\n",
-         beg, end, old_mid, new_mid);
-  uptr granularity = SHADOW_GRANULARITY;
-  if (!IsAligned(beg, granularity))
-    Report("ERROR: beg is not aligned by %d\n", granularity);
-  stack->Print();
-  ReportErrorSummary("bad-__sanitizer_annotate_contiguous_container", stack);
+  ErrorBadParamsToAnnotateContiguousContainer error(
+      GetCurrentTidOrInvalid(), stack, beg, end, old_mid, new_mid);
+  in_report.ReportError(error);
 }
 
 void ReportODRViolation(const __asan_global *g1, u32 stack_id1,
                         const __asan_global *g2, u32 stack_id2) {
   ScopedInErrorReport in_report;
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: odr-violation (%p):\n", g1->beg);
-  Printf("%s", d.EndWarning());
-  InternalScopedString g1_loc(256), g2_loc(256);
-  PrintGlobalLocation(&g1_loc, *g1);
-  PrintGlobalLocation(&g2_loc, *g2);
-  Printf("  [1] size=%zd '%s' %s\n", g1->size,
-         MaybeDemangleGlobalName(g1->name), g1_loc.data());
-  Printf("  [2] size=%zd '%s' %s\n", g2->size,
-         MaybeDemangleGlobalName(g2->name), g2_loc.data());
-  if (stack_id1 && stack_id2) {
-    Printf("These globals were registered at these points:\n");
-    Printf("  [1]:\n");
-    StackDepotGet(stack_id1).Print();
-    Printf("  [2]:\n");
-    StackDepotGet(stack_id2).Print();
-  }
-  Report("HINT: if you don't care about these errors you may set "
-         "ASAN_OPTIONS=detect_odr_violation=0\n");
-  InternalScopedString error_msg(256);
-  error_msg.append("odr-violation: global '%s' at %s",
-                   MaybeDemangleGlobalName(g1->name), g1_loc.data());
-  ReportErrorSummary(error_msg.data());
+  ErrorODRViolation error(GetCurrentTidOrInvalid(), g1, stack_id1, g2,
+                          stack_id2);
+  in_report.ReportError(error);
 }
 
 // ----------------------- CheckForInvalidPointerPair ----------- {{{1
-static NOINLINE void
-ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp, uptr a1, uptr a2) {
+static NOINLINE void ReportInvalidPointerPair(uptr pc, uptr bp, uptr sp,
+                                              uptr a1, uptr a2) {
   ScopedInErrorReport in_report;
-  const char *bug_type = "invalid-pointer-pair";
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: invalid-pointer-pair: %p %p\n", a1, a2);
-  Printf("%s", d.EndWarning());
-  GET_STACK_TRACE_FATAL(pc, bp);
-  stack.Print();
-  DescribeAddress(a1, 1, bug_type);
-  DescribeAddress(a2, 1, bug_type);
-  ReportErrorSummary(bug_type, &stack);
+  ErrorInvalidPointerPair error(GetCurrentTidOrInvalid(), pc, bp, sp, a1, a2);
+  in_report.ReportError(error);
 }
 
 static INLINE void CheckForInvalidPointerPair(void *p1, void *p2) {
@@ -903,7 +365,7 @@
              addr);
   PrintZoneForPointer(addr, zone_ptr, zone_name);
   stack->Print();
-  DescribeHeapAddress(addr, 1);
+  DescribeAddressIfHeap(addr);
 }
 
 // -------------- SuppressErrorReport -------------- {{{1
@@ -920,34 +382,10 @@
   Die();
 }
 
-static void PrintContainerOverflowHint() {
-  Printf("HINT: if you don't care about these errors you may set "
-         "ASAN_OPTIONS=detect_container_overflow=0.\n"
-         "If you suspect a false positive see also: "
-         "https://github.com/google/sanitizers/wiki/"
-         "AddressSanitizerContainerOverflow.\n");
-}
-
-static bool AdjacentShadowValuesAreFullyPoisoned(u8 *s) {
-  return s[-1] > 127 && s[1] > 127;
-}
-
 void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
                         uptr access_size, u32 exp, bool fatal) {
   if (!fatal && SuppressErrorReport(pc)) return;
   ENABLE_FRAME_POINTER;
-  ScarinessScore SS;
-
-  if (access_size) {
-    if (access_size <= 9) {
-      char desr[] = "?-byte";
-      desr[0] = '0' + access_size;
-      SS.Scare(access_size + access_size / 2, desr);
-    } else if (access_size >= 10) {
-      SS.Scare(15, "multi-byte");
-    }
-    is_write ? SS.Scare(20, "write") : SS.Scare(1, "read");
-  }
 
   // Optimization experiments.
   // The experiments can be used to evaluate potential optimizations that remove
@@ -958,118 +396,10 @@
   // The reaction to a non-zero value of exp is to be defined.
   (void)exp;
 
-  // Determine the error type.
-  const char *bug_descr = "unknown-crash";
-  u8 shadow_val = 0;
-  if (AddrIsInMem(addr)) {
-    u8 *shadow_addr = (u8*)MemToShadow(addr);
-    // If we are accessing 16 bytes, look at the second shadow byte.
-    if (*shadow_addr == 0 && access_size > SHADOW_GRANULARITY)
-      shadow_addr++;
-    // If we are in the partial right redzone, look at the next shadow byte.
-    if (*shadow_addr > 0 && *shadow_addr < 128)
-      shadow_addr++;
-    bool far_from_bounds = false;
-    shadow_val = *shadow_addr;
-    int bug_type_score = 0;
-    // For use-after-frees reads are almost as bad as writes.
-    int read_after_free_bonus = 0;
-    switch (shadow_val) {
-      case kAsanHeapLeftRedzoneMagic:
-      case kAsanHeapRightRedzoneMagic:
-      case kAsanArrayCookieMagic:
-        bug_descr = "heap-buffer-overflow";
-        bug_type_score = 10;
-        far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
-        break;
-      case kAsanHeapFreeMagic:
-        bug_descr = "heap-use-after-free";
-        bug_type_score = 20;
-        if (!is_write) read_after_free_bonus = 18;
-        break;
-      case kAsanStackLeftRedzoneMagic:
-        bug_descr = "stack-buffer-underflow";
-        bug_type_score = 25;
-        far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
-        break;
-      case kAsanInitializationOrderMagic:
-        bug_descr = "initialization-order-fiasco";
-        bug_type_score = 1;
-        break;
-      case kAsanStackMidRedzoneMagic:
-      case kAsanStackRightRedzoneMagic:
-      case kAsanStackPartialRedzoneMagic:
-        bug_descr = "stack-buffer-overflow";
-        bug_type_score = 25;
-        far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
-        break;
-      case kAsanStackAfterReturnMagic:
-        bug_descr = "stack-use-after-return";
-        bug_type_score = 30;
-        if (!is_write) read_after_free_bonus = 18;
-        break;
-      case kAsanUserPoisonedMemoryMagic:
-        bug_descr = "use-after-poison";
-        bug_type_score = 20;
-        break;
-      case kAsanContiguousContainerOOBMagic:
-        bug_descr = "container-overflow";
-        bug_type_score = 10;
-        break;
-      case kAsanStackUseAfterScopeMagic:
-        bug_descr = "stack-use-after-scope";
-        bug_type_score = 10;
-        break;
-      case kAsanGlobalRedzoneMagic:
-        bug_descr = "global-buffer-overflow";
-        bug_type_score = 10;
-        far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
-        break;
-      case kAsanIntraObjectRedzone:
-        bug_descr = "intra-object-overflow";
-        bug_type_score = 10;
-        break;
-      case kAsanAllocaLeftMagic:
-      case kAsanAllocaRightMagic:
-        bug_descr = "dynamic-stack-buffer-overflow";
-        bug_type_score = 25;
-        far_from_bounds = AdjacentShadowValuesAreFullyPoisoned(shadow_addr);
-        break;
-    }
-    SS.Scare(bug_type_score + read_after_free_bonus, bug_descr);
-    if (far_from_bounds)
-      SS.Scare(10, "far-from-bounds");
-  }
-
-  ReportData report = { pc, sp, bp, addr, (bool)is_write, access_size,
-                        bug_descr };
-  ScopedInErrorReport in_report(&report, fatal);
-
-  Decorator d;
-  Printf("%s", d.Warning());
-  Report("ERROR: AddressSanitizer: %s on address "
-             "%p at pc %p bp %p sp %p\n",
-             bug_descr, (void*)addr, pc, bp, sp);
-  Printf("%s", d.EndWarning());
-
-  u32 curr_tid = GetCurrentTidOrInvalid();
-  char tname[128];
-  Printf("%s%s of size %zu at %p thread T%d%s%s\n",
-         d.Access(),
-         access_size ? (is_write ? "WRITE" : "READ") : "ACCESS",
-         access_size, (void*)addr, curr_tid,
-         ThreadNameWithParenthesis(curr_tid, tname, sizeof(tname)),
-         d.EndAccess());
-
-  SS.Print();
-  GET_STACK_TRACE_FATAL(pc, bp);
-  stack.Print();
-
-  DescribeAddress(addr, access_size, bug_descr);
-  if (shadow_val == kAsanContiguousContainerOOBMagic)
-    PrintContainerOverflowHint();
-  ReportErrorSummary(bug_descr, &stack);
-  PrintShadowMemoryForAddress(addr);
+  ScopedInErrorReport in_report(fatal);
+  ErrorGeneric error(GetCurrentTidOrInvalid(), pc, bp, sp, addr, is_write,
+                     access_size);
+  in_report.ReportError(error);
 }
 
 }  // namespace __asan
@@ -1092,40 +422,55 @@
 void __asan_describe_address(uptr addr) {
   // Thread registry must be locked while we're describing an address.
   asanThreadRegistry().Lock();
-  DescribeAddress(addr, 1, "");
+  PrintAddressDescription(addr, 1, "");
   asanThreadRegistry().Unlock();
 }
 
 int __asan_report_present() {
-  return report_happened ? 1 : 0;
+  return ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric;
 }
 
 uptr __asan_get_report_pc() {
-  return report_data.pc;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError().Generic.pc;
+  return 0;
 }
 
 uptr __asan_get_report_bp() {
-  return report_data.bp;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError().Generic.bp;
+  return 0;
 }
 
 uptr __asan_get_report_sp() {
-  return report_data.sp;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError().Generic.sp;
+  return 0;
 }
 
 uptr __asan_get_report_address() {
-  return report_data.addr;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError()
+        .Generic.addr_description.Address();
+  return 0;
 }
 
 int __asan_get_report_access_type() {
-  return report_data.is_write ? 1 : 0;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError().Generic.is_write;
+  return 0;
 }
 
 uptr __asan_get_report_access_size() {
-  return report_data.access_size;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError().Generic.access_size;
+  return 0;
 }
 
 const char *__asan_get_report_description() {
-  return report_data.description;
+  if (ScopedInErrorReport::CurrentError().kind == kErrorKindGeneric)
+    return ScopedInErrorReport::CurrentError().Generic.bug_descr;
+  return nullptr;
 }
 
 extern "C" {
diff --git a/lib/asan/asan_report.h b/lib/asan/asan_report.h
index 7b7122f..5ebfda6 100644
--- a/lib/asan/asan_report.h
+++ b/lib/asan/asan_report.h
@@ -25,32 +25,29 @@
   uptr name_len;
 };
 
-struct AddressDescription {
-  char *name;
-  uptr name_size;
-  uptr region_address;
-  uptr region_size;
-  const char *region_kind;
-};
-
 // Returns the number of globals close to the provided address and copies
 // them to "globals" array.
 int GetGlobalsForAddress(uptr addr, __asan_global *globals, u32 *reg_sites,
                          int max_globals);
-bool GetInfoForAddressIfGlobal(uptr addr, AddressDescription *descr);
+
+const char *MaybeDemangleGlobalName(const char *name);
+void PrintGlobalNameIfASCII(InternalScopedString *str, const __asan_global &g);
+void PrintGlobalLocation(InternalScopedString *str, const __asan_global &g);
+
+void PrintMemoryByte(InternalScopedString *str, const char *before, u8 byte,
+                     bool in_shadow, const char *after = "\n");
+
 // The following functions prints address description depending
 // on the memory type (shadow/heap/stack/global).
-void DescribeHeapAddress(uptr addr, uptr access_size);
 bool ParseFrameDescription(const char *frame_descr,
                            InternalMmapVector<StackVarDescr> *vars);
-bool DescribeAddressIfStack(uptr addr, uptr access_size);
 
 // Different kinds of error reports.
 void ReportGenericError(uptr pc, uptr bp, uptr sp, uptr addr, bool is_write,
                         uptr access_size, u32 exp, bool fatal);
 void ReportStackOverflow(const SignalContext &sig);
-void ReportDeadlySignal(const char *description, const SignalContext &sig);
-void ReportNewDeleteSizeMismatch(uptr addr, uptr alloc_size, uptr delete_size,
+void ReportDeadlySignal(int signo, const SignalContext &sig);
+void ReportNewDeleteSizeMismatch(uptr addr, uptr delete_size,
                                  BufferedStackTrace *free_stack);
 void ReportDoubleFree(uptr addr, BufferedStackTrace *free_stack);
 void ReportFreeNotMalloced(uptr addr, BufferedStackTrace *free_stack);
diff --git a/lib/asan/asan_scariness_score.h b/lib/asan/asan_scariness_score.h
index 492eb56..7f15714 100644
--- a/lib/asan/asan_scariness_score.h
+++ b/lib/asan/asan_scariness_score.h
@@ -34,10 +34,10 @@
 #include "sanitizer_common/sanitizer_libc.h"
 
 namespace __asan {
-class ScarinessScore {
- public:
-  ScarinessScore() {
+struct ScarinessScoreBase {
+  void Clear() {
     descr[0] = 0;
+    score = 0;
   }
   void Scare(int add_to_score, const char *reason) {
     if (descr[0])
@@ -52,16 +52,23 @@
       Printf("SCARINESS: %d (%s)\n", score, descr);
   }
   static void PrintSimple(int score, const char *descr) {
-    ScarinessScore SS;
-    SS.Scare(score, descr);
-    SS.Print();
+    ScarinessScoreBase SSB;
+    SSB.Clear();
+    SSB.Scare(score, descr);
+    SSB.Print();
   }
 
  private:
-  int score = 0;
+  int score;
   char descr[1024];
 };
 
+struct ScarinessScore : ScarinessScoreBase {
+  ScarinessScore() {
+    Clear();
+  }
+};
+
 }  // namespace __asan
 
 #endif  // ASAN_SCARINESS_SCORE_H
diff --git a/lib/asan/asan_win.cc b/lib/asan/asan_win.cc
index cb8ca58..4881f83 100644
--- a/lib/asan/asan_win.cc
+++ b/lib/asan/asan_win.cc
@@ -282,7 +282,10 @@
 }
 
 // Return the textual name for this exception.
-static const char *DescribeDeadlyException(unsigned code) {
+const char *DescribeSignalOrException(int signo) {
+  unsigned code = signo;
+  // Get the string description of the exception if this is a known deadly
+  // exception.
   switch (code) {
     case EXCEPTION_ACCESS_VIOLATION:
       return "access-violation";
@@ -301,12 +304,8 @@
   CONTEXT *context = info->ContextRecord;
 
   if (ShouldReportDeadlyException(exception_record->ExceptionCode)) {
-    // Get the string description of the exception if this is a known deadly
-    // exception.
-    const char *description =
-        DescribeDeadlyException(exception_record->ExceptionCode);
     SignalContext sig = SignalContext::Create(exception_record, context);
-    ReportDeadlySignal(description, sig);
+    ReportDeadlySignal(exception_record->ExceptionCode, sig);
   }
 
   // FIXME: Handle EXCEPTION_STACK_OVERFLOW here.
diff --git a/lib/sanitizer_common/sanitizer_common.h b/lib/sanitizer_common/sanitizer_common.h
index bcb4929..d280a05 100644
--- a/lib/sanitizer_common/sanitizer_common.h
+++ b/lib/sanitizer_common/sanitizer_common.h
@@ -392,7 +392,7 @@
 //   error_type file:line[:column][ function]
 void ReportErrorSummary(const char *error_type, const AddressInfo &info);
 // Same as above, but obtains AddressInfo by symbolizing top stack trace frame.
-void ReportErrorSummary(const char *error_type, StackTrace *trace);
+void ReportErrorSummary(const char *error_type, const StackTrace *trace);
 
 // Math
 #if SANITIZER_WINDOWS && !defined(__clang__) && !defined(__GNUC__)
diff --git a/lib/sanitizer_common/sanitizer_common_libcdep.cc b/lib/sanitizer_common/sanitizer_common_libcdep.cc
index 9472a1d..27876f4 100644
--- a/lib/sanitizer_common/sanitizer_common_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_common_libcdep.cc
@@ -46,7 +46,7 @@
   sandboxing_callback = f;
 }
 
-void ReportErrorSummary(const char *error_type, StackTrace *stack) {
+void ReportErrorSummary(const char *error_type, const StackTrace *stack) {
 #if !SANITIZER_GO
   if (!common_flags()->print_summary)
     return;
diff --git a/lib/sanitizer_common/sanitizer_posix_libcdep.cc b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
index f1e8b50..e6c5fcc 100644
--- a/lib/sanitizer_common/sanitizer_posix_libcdep.cc
+++ b/lib/sanitizer_common/sanitizer_posix_libcdep.cc
@@ -44,6 +44,8 @@
 #define MAP_NORESERVE 0
 #endif
 
+typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
+
 namespace __sanitizer {
 
 u32 GetUid() {
@@ -126,6 +128,14 @@
 }
 
 void Abort() {
+  // If we are handling SIGABRT, unhandle it first.
+  if (IsHandledDeadlySignal(SIGABRT)) {
+    struct sigaction sigact;
+    internal_memset(&sigact, 0, sizeof(sigact));
+    sigact.sa_sigaction = (sa_sigaction_t)SIG_DFL;
+    internal_sigaction(SIGABRT, &sigact, nullptr);
+  }
+
   abort();
 }
 
@@ -170,7 +180,6 @@
   UnmapOrDie(oldstack.ss_sp, oldstack.ss_size);
 }
 
-typedef void (*sa_sigaction_t)(int, siginfo_t *, void *);
 static void MaybeInstallSigaction(int signum,
                                   SignalHandlerType handler) {
   if (!IsHandledDeadlySignal(signum))
diff --git a/test/asan/TestCases/Posix/handle_abort_on_error.cc b/test/asan/TestCases/Posix/handle_abort_on_error.cc
new file mode 100644
index 0000000..fa8cdd4
--- /dev/null
+++ b/test/asan/TestCases/Posix/handle_abort_on_error.cc
@@ -0,0 +1,9 @@
+// Regression test: this used to abort() in SIGABRT handler in an infinite loop.
+// RUN: %clangxx_asan -O0 %s -o %t && %env_asan_opts=handle_abort=1,abort_on_error=1 not --crash %run %t 2>&1 | FileCheck %s
+
+#include <stdlib.h>
+
+int main() {
+  abort();
+  // CHECK: ERROR: AddressSanitizer: ABRT
+}
diff --git a/test/asan/TestCases/global-address.cpp b/test/asan/TestCases/global-address.cpp
new file mode 100644
index 0000000..0e56ca1
--- /dev/null
+++ b/test/asan/TestCases/global-address.cpp
@@ -0,0 +1,12 @@
+// RUN: %clangxx_asan -o %t %s
+// RUN: not %run %t 2>&1 | FileCheck %s
+#include <sanitizer/allocator_interface.h>
+
+int g_i = 42;
+int main() {
+  // CHECK: AddressSanitizer: attempting to call __sanitizer_get_allocated_size() for pointer which is not owned
+  // CHECK-NOT: ASAN:DEADLYSIGNAL
+  // CHECK: SUMMARY: AddressSanitizer: bad-__sanitizer_get_allocated_size
+  // CHECK-NOT: ASAN:DEADLYSIGNAL
+  return (int)__sanitizer_get_allocated_size(&g_i);
+}
diff --git a/test/asan/TestCases/Linux/scariness_score_test.cc b/test/asan/TestCases/scariness_score_test.cc
similarity index 89%
rename from test/asan/TestCases/Linux/scariness_score_test.cc
rename to test/asan/TestCases/scariness_score_test.cc
index 2485413..dee7a13 100644
--- a/test/asan/TestCases/Linux/scariness_score_test.cc
+++ b/test/asan/TestCases/scariness_score_test.cc
@@ -1,7 +1,9 @@
 // Test how we produce the scariness score.
 
 // RUN: %clangxx_asan -O0 %s -o %t
-// RUN: export %env_asan_opts=detect_stack_use_after_return=1:handle_abort=1:print_scariness=1
+// On OSX and Windows, alloc_dealloc_mismatch=1 isn't 100% reliable, so it's
+// off by default. It's safe for these tests, though, so we turn it on.
+// RUN: export %env_asan_opts=detect_stack_use_after_return=1:handle_abort=1:print_scariness=1:alloc_dealloc_mismatch=1
 // Make sure the stack is limited (may not be the default under GNU make)
 // RUN: ulimit -s 4096
 // RUN: not %run %t  1 2>&1 | FileCheck %s --check-prefix=CHECK1
@@ -155,12 +157,12 @@
     case 19: *zero_ptr = 0; break;
     case 20: *wild_addr = 0; break;
     case 21: zero = *wild_addr; break;
-    case 22: abort(); break;
-    case 23: ((void (*)(void))wild_addr)(); break;
-    case 24: delete (new int[10]); break;
-    case 25: free((char*)malloc(100) + 10); break;
-    case 26: memcpy(arr, arr+10, 20);  break;
-    case 27: UseAfterPoison(); break;
+    case 22: ((void (*)(void))wild_addr)(); break;
+    case 23: delete (new int[10]); break;
+    case 24: free((char*)malloc(100) + 10); break;
+    case 25: memcpy(arr, arr+10, 20);  break;
+    case 26: UseAfterPoison(); break;
+    case 27: abort();
     // CHECK1: SCARINESS: 12 (1-byte-read-heap-buffer-overflow)
     // CHECK2: SCARINESS: 17 (4-byte-read-heap-buffer-overflow)
     // CHECK3: SCARINESS: 33 (2-byte-write-heap-buffer-overflow)
@@ -182,11 +184,11 @@
     // CHECK19: SCARINESS: 10 (null-deref)
     // CHECK20: SCARINESS: 30 (wild-addr-write)
     // CHECK21: SCARINESS: 20 (wild-addr-read)
-    // CHECK22: SCARINESS: 10 (signal)
-    // CHECK23: SCARINESS: 60 (wild-jump)
-    // CHECK24: SCARINESS: 10 (alloc-dealloc-mismatch)
-    // CHECK25: SCARINESS: 40 (bad-free)
-    // CHECK26: SCARINESS: 10 (memcpy-param-overlap)
-    // CHECK27: SCARINESS: 27 (4-byte-read-use-after-poison)
+    // CHECK22: SCARINESS: 60 (wild-jump)
+    // CHECK23: SCARINESS: 10 (alloc-dealloc-mismatch)
+    // CHECK24: SCARINESS: 40 (bad-free)
+    // CHECK25: SCARINESS: 10 (memcpy-param-overlap)
+    // CHECK26: SCARINESS: 27 (4-byte-read-use-after-poison)
+    // CHECK27: SCARINESS: 10 (signal)
   }
 }