brintos

brintos / llvm-project-archived public Read only

0
0
Text · 39.6 KiB · 871aa74 Raw
1142 lines · cpp
1//===-- hwasan_report.cpp -------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of HWAddressSanitizer.10//11// Error reporting.12//===----------------------------------------------------------------------===//13 14#include "hwasan_report.h"15 16#include <dlfcn.h>17 18#include "hwasan.h"19#include "hwasan_allocator.h"20#include "hwasan_globals.h"21#include "hwasan_mapping.h"22#include "hwasan_thread.h"23#include "hwasan_thread_list.h"24#include "sanitizer_common/sanitizer_allocator_internal.h"25#include "sanitizer_common/sanitizer_array_ref.h"26#include "sanitizer_common/sanitizer_common.h"27#include "sanitizer_common/sanitizer_flags.h"28#include "sanitizer_common/sanitizer_internal_defs.h"29#include "sanitizer_common/sanitizer_mutex.h"30#include "sanitizer_common/sanitizer_placement_new.h"31#include "sanitizer_common/sanitizer_report_decorator.h"32#include "sanitizer_common/sanitizer_stackdepot.h"33#include "sanitizer_common/sanitizer_stacktrace_printer.h"34#include "sanitizer_common/sanitizer_symbolizer.h"35 36using namespace __sanitizer;37 38namespace __hwasan {39 40class ScopedReport {41 public:42  explicit ScopedReport(bool fatal) : fatal(fatal) {43    Lock lock(&error_message_lock_);44    error_message_ptr_ = &error_message_;45    ++hwasan_report_count;46  }47 48  ~ScopedReport() {49    void (*report_cb)(const char *);50    {51      Lock lock(&error_message_lock_);52      report_cb = error_report_callback_;53      error_message_ptr_ = nullptr;54    }55    if (report_cb)56      report_cb(error_message_.data());57    if (fatal)58      SetAbortMessage(error_message_.data());59    if (common_flags()->print_module_map >= 2 ||60        (fatal && common_flags()->print_module_map))61      DumpProcessMap();62    if (fatal)63      Die();64  }65 66  static void MaybeAppendToErrorMessage(const char *msg) {67    Lock lock(&error_message_lock_);68    if (!error_message_ptr_)69      return;70    error_message_ptr_->Append(msg);71  }72 73  static void SetErrorReportCallback(void (*callback)(const char *)) {74    Lock lock(&error_message_lock_);75    error_report_callback_ = callback;76  }77 78 private:79  InternalScopedString error_message_;80  bool fatal;81 82  static Mutex error_message_lock_;83  static InternalScopedString *error_message_ptr_84      SANITIZER_GUARDED_BY(error_message_lock_);85  static void (*error_report_callback_)(const char *);86};87 88Mutex ScopedReport::error_message_lock_;89InternalScopedString *ScopedReport::error_message_ptr_;90void (*ScopedReport::error_report_callback_)(const char *);91 92// If there is an active ScopedReport, append to its error message.93void AppendToErrorMessageBuffer(const char *buffer) {94  ScopedReport::MaybeAppendToErrorMessage(buffer);95}96 97static StackTrace GetStackTraceFromId(u32 id) {98  CHECK(id);99  StackTrace res = StackDepotGet(id);100  CHECK(res.trace);101  return res;102}103 104static void MaybePrintAndroidHelpUrl() {105#if SANITIZER_ANDROID106  Printf(107      "Learn more about HWASan reports: "108      "https://source.android.com/docs/security/test/memory-safety/"109      "hwasan-reports\n");110#endif111}112 113namespace {114// A RAII object that holds a copy of the current thread stack ring buffer.115// The actual stack buffer may change while we are iterating over it (for116// example, Printf may call syslog() which can itself be built with hwasan).117class SavedStackAllocations {118 public:119  SavedStackAllocations() = default;120 121  explicit SavedStackAllocations(Thread *t) { CopyFrom(t); }122 123  void CopyFrom(Thread *t) {124    StackAllocationsRingBuffer *rb = t->stack_allocations();125    uptr size = rb->size() * sizeof(uptr);126    void *storage =127        MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");128    new (&rb_) StackAllocationsRingBuffer(*rb, storage);129    thread_id_ = t->unique_id();130  }131 132  ~SavedStackAllocations() {133    if (rb_) {134      StackAllocationsRingBuffer *rb = get();135      UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));136    }137  }138 139  const StackAllocationsRingBuffer *get() const {140    return (const StackAllocationsRingBuffer *)&rb_;141  }142 143  StackAllocationsRingBuffer *get() {144    return (StackAllocationsRingBuffer *)&rb_;145  }146 147  u32 thread_id() const { return thread_id_; }148 149 private:150  uptr rb_ = 0;151  u32 thread_id_;152};153 154class Decorator: public __sanitizer::SanitizerCommonDecorator {155 public:156  Decorator() : SanitizerCommonDecorator() { }157  const char *Access() { return Blue(); }158  const char *Allocation() const { return Magenta(); }159  const char *Origin() const { return Magenta(); }160  const char *Name() const { return Green(); }161  const char *Location() { return Green(); }162  const char *Thread() { return Green(); }163};164}  // namespace165 166static bool FindHeapAllocation(HeapAllocationsRingBuffer *rb, uptr tagged_addr,167                               HeapAllocationRecord *har, uptr *ring_index,168                               uptr *num_matching_addrs,169                               uptr *num_matching_addrs_4b) {170  if (!rb) return false;171 172  *num_matching_addrs = 0;173  *num_matching_addrs_4b = 0;174  for (uptr i = 0, size = rb->size(); i < size; i++) {175    auto h = (*rb)[i];176    if (h.tagged_addr <= tagged_addr &&177        h.tagged_addr + h.requested_size > tagged_addr) {178      *har = h;179      *ring_index = i;180      return true;181    }182 183    // Measure the number of heap ring buffer entries that would have matched184    // if we had only one entry per address (e.g. if the ring buffer data was185    // stored at the address itself). This will help us tune the allocator186    // implementation for MTE.187    if (UntagAddr(h.tagged_addr) <= UntagAddr(tagged_addr) &&188        UntagAddr(h.tagged_addr) + h.requested_size > UntagAddr(tagged_addr)) {189      ++*num_matching_addrs;190    }191 192    // Measure the number of heap ring buffer entries that would have matched193    // if we only had 4 tag bits, which is the case for MTE.194    auto untag_4b = [](uptr p) {195      return p & ((1ULL << 60) - 1);196    };197    if (untag_4b(h.tagged_addr) <= untag_4b(tagged_addr) &&198        untag_4b(h.tagged_addr) + h.requested_size > untag_4b(tagged_addr)) {199      ++*num_matching_addrs_4b;200    }201  }202  return false;203}204 205static void PrintStackAllocations(const StackAllocationsRingBuffer *sa,206                                  tag_t addr_tag, uptr untagged_addr) {207  uptr frames = Min((uptr)flags()->stack_history_size, sa->size());208  bool found_local = false;209  InternalScopedString location;210  for (uptr i = 0; i < frames; i++) {211    const uptr *record_addr = &(*sa)[i];212    uptr record = *record_addr;213    if (!record)214      break;215    tag_t base_tag =216        reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift;217    const uptr fp = (record >> kRecordFPShift) << kRecordFPLShift;218    CHECK_LT(fp, kRecordFPModulus);219    uptr pc_mask = (1ULL << kRecordFPShift) - 1;220    uptr pc = record & pc_mask;221    FrameInfo frame;222    if (!Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame))223      continue;224    for (LocalInfo &local : frame.locals) {225      if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)226        continue;227      if (!(local.name && internal_strlen(local.name)) &&228          !(local.function_name && internal_strlen(local.function_name)) &&229          !(local.decl_file && internal_strlen(local.decl_file)))230        continue;231      tag_t obj_tag = base_tag ^ local.tag_offset;232      if (obj_tag != addr_tag)233        continue;234 235      // We only store bits 4-19 of FP (bits 0-3 are guaranteed to be zero).236      // So we know only `FP % kRecordFPModulus`, and we can only calculate237      // `local_beg % kRecordFPModulus`.238      // Out of all possible `local_beg` we will only consider 2 candidates239      // nearest to the `untagged_addr`.240      uptr local_beg_mod = (fp + local.frame_offset) % kRecordFPModulus;241      // Pick `local_beg` in the same 1 MiB block as `untagged_addr`.242      uptr local_beg =243          RoundDownTo(untagged_addr, kRecordFPModulus) + local_beg_mod;244      // Pick the largest `local_beg <= untagged_addr`. It's either the current245      // one or the one before.246      if (local_beg > untagged_addr)247        local_beg -= kRecordFPModulus;248 249      uptr offset = -1ull;250      const char *whence;251      const char *cause = nullptr;252      uptr best_beg;253 254      // Try two 1 MiB blocks options and pick nearest one.255      for (uptr i = 0; i < 2; ++i, local_beg += kRecordFPModulus) {256        uptr local_end = local_beg + local.size;257        if (local_beg > local_end)258          continue;  // This is a wraparound.259        if (local_beg <= untagged_addr && untagged_addr < local_end) {260          offset = untagged_addr - local_beg;261          whence = "inside";262          cause = "use-after-scope";263          best_beg = local_beg;264          break;  // This is as close at it can be.265        }266 267        if (untagged_addr >= local_end) {268          uptr new_offset = untagged_addr - local_end;269          if (new_offset < offset) {270            offset = new_offset;271            whence = "after";272            cause = "stack-buffer-overflow";273            best_beg = local_beg;274          }275        } else {276          uptr new_offset = local_beg - untagged_addr;277          if (new_offset < offset) {278            offset = new_offset;279            whence = "before";280            cause = "stack-buffer-overflow";281            best_beg = local_beg;282          }283        }284      }285 286      // To fail the `untagged_addr` must be near nullptr, which is impossible287      // with Linux user space memory layout.288      if (!cause)289        continue;290 291      if (!found_local) {292        Printf("\nPotentially referenced stack objects:\n");293        found_local = true;294      }295 296      Decorator d;297      Printf("%s", d.Error());298      Printf("Cause: %s\n", cause);299      Printf("%s", d.Default());300      Printf("%s", d.Location());301      StackTracePrinter::GetOrInit()->RenderSourceLocation(302          &location, local.decl_file, local.decl_line, /* column= */ 0,303          common_flags()->symbolize_vs_style,304          common_flags()->strip_path_prefix);305      Printf(306          "%p is located %zd bytes %s a %zd-byte local variable %s "307          "[%p,%p) "308          "in %s %s\n",309          (void *)untagged_addr, offset, whence, local.size, local.name,310          (void *)best_beg, (void *)(best_beg + local.size),311          local.function_name, location.data());312      location.clear();313      Printf("%s\n", d.Default());314    }315    frame.Clear();316  }317 318  if (found_local)319    return;320 321  // We didn't find any locals. Most likely we don't have symbols, so dump322  // the information that we have for offline analysis.323  InternalScopedString frame_desc;324  Printf("Previously allocated frames:\n");325  for (uptr i = 0; i < frames; i++) {326    const uptr *record_addr = &(*sa)[i];327    uptr record = *record_addr;328    if (!record)329      break;330    uptr pc_mask = (1ULL << 48) - 1;331    uptr pc = record & pc_mask;332    frame_desc.AppendF("  record_addr:%p record:0x%zx",333                       reinterpret_cast<const void *>(record_addr), record);334    SymbolizedStackHolder symbolized_stack(335        Symbolizer::GetOrInit()->SymbolizePC(pc));336    const SymbolizedStack *frame = symbolized_stack.get();337    if (frame) {338      StackTracePrinter::GetOrInit()->RenderFrame(339          &frame_desc, " %F %L", 0, frame->info.address, &frame->info,340          common_flags()->symbolize_vs_style,341          common_flags()->strip_path_prefix);342    }343    Printf("%s\n", frame_desc.data());344    frame_desc.clear();345  }346}347 348// Returns true if tag == *tag_ptr, reading tags from short granules if349// necessary. This may return a false positive if tags 1-15 are used as a350// regular tag rather than a short granule marker.351static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {352  if (tag == *tag_ptr)353    return true;354  if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1)355    return false;356  uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr));357  tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1);358  return tag == inline_tag;359}360 361// HWASan globals store the size of the global in the descriptor. In cases where362// we don't have a binary with symbols, we can't grab the size of the global363// from the debug info - but we might be able to retrieve it from the364// descriptor. Returns zero if the lookup failed.365static uptr GetGlobalSizeFromDescriptor(uptr ptr) {366  // Find the ELF object that this global resides in.367  Dl_info info;368  if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)369    return 0;370  auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);371  auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(372      reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);373 374  // Get the load bias. This is normally the same as the dli_fbase address on375  // position-independent code, but can be different on non-PIE executables,376  // binaries using LLD's partitioning feature, or binaries compiled with a377  // linker script.378  ElfW(Addr) load_bias = 0;379  for (const auto &phdr :380       ArrayRef<const ElfW(Phdr)>(phdr_begin, phdr_begin + ehdr->e_phnum)) {381    if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)382      continue;383    load_bias = reinterpret_cast<ElfW(Addr)>(ehdr) - phdr.p_vaddr;384    break;385  }386 387  // Walk all globals in this ELF object, looking for the one we're interested388  // in. Once we find it, we can stop iterating and return the size of the389  // global we're interested in.390  for (const hwasan_global &global :391       HwasanGlobalsFor(load_bias, phdr_begin, ehdr->e_phnum))392    if (global.addr() <= ptr && ptr < global.addr() + global.size())393      return global.size();394 395  return 0;396}397 398void ReportStats() {}399 400constexpr uptr kDumpWidth = 16;401constexpr uptr kShadowLines = 17;402constexpr uptr kShadowDumpSize = kShadowLines * kDumpWidth;403 404constexpr uptr kShortLines = 3;405constexpr uptr kShortDumpSize = kShortLines * kDumpWidth;406constexpr uptr kShortDumpOffset = (kShadowLines - kShortLines) / 2 * kDumpWidth;407 408static uptr GetPrintTagStart(uptr addr) {409  addr = MemToShadow(addr);410  addr = RoundDownTo(addr, kDumpWidth);411  addr -= kDumpWidth * (kShadowLines / 2);412  return addr;413}414 415template <typename PrintTag>416static void PrintTagInfoAroundAddr(uptr addr, uptr num_rows,417                                   InternalScopedString &s,418                                   PrintTag print_tag) {419  uptr center_row_beg = RoundDownTo(addr, kDumpWidth);420  uptr beg_row = center_row_beg - kDumpWidth * (num_rows / 2);421  uptr end_row = center_row_beg + kDumpWidth * ((num_rows + 1) / 2);422  for (uptr row = beg_row; row < end_row; row += kDumpWidth) {423    s.Append(row == center_row_beg ? "=>" : "  ");424    s.AppendF("%p:", (void *)ShadowToMem(row));425    for (uptr i = 0; i < kDumpWidth; i++) {426      s.Append(row + i == addr ? "[" : " ");427      print_tag(s, row + i);428      s.Append(row + i == addr ? "]" : " ");429    }430    s.Append("\n");431  }432}433 434template <typename GetTag, typename GetShortTag>435static void PrintTagsAroundAddr(uptr addr, GetTag get_tag,436                                GetShortTag get_short_tag) {437  InternalScopedString s;438  addr = MemToShadow(addr);439  s.AppendF(440      "\nMemory tags around the buggy address (one tag corresponds to %zd "441      "bytes):\n",442      kShadowAlignment);443  PrintTagInfoAroundAddr(addr, kShadowLines, s,444                         [&](InternalScopedString &s, uptr tag_addr) {445                           tag_t tag = get_tag(tag_addr);446                           s.AppendF("%02x", tag);447                         });448 449  s.AppendF(450      "Tags for short granules around the buggy address (one tag corresponds "451      "to %zd bytes):\n",452      kShadowAlignment);453  PrintTagInfoAroundAddr(addr, kShortLines, s,454                         [&](InternalScopedString &s, uptr tag_addr) {455                           tag_t tag = get_tag(tag_addr);456                           if (tag >= 1 && tag <= kShadowAlignment) {457                             tag_t short_tag = get_short_tag(tag_addr);458                             s.AppendF("%02x", short_tag);459                           } else {460                             s.Append("..");461                           }462                         });463  s.Append(464      "See "465      "https://clang.llvm.org/docs/"466      "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "467      "description of short granule tags\n");468  Printf("%s", s.data());469}470 471static uptr GetTopPc(const StackTrace *stack) {472  return stack->size ? StackTrace::GetPreviousInstructionPc(stack->trace[0])473                     : 0;474}475 476namespace {477class BaseReport {478 public:479  BaseReport(StackTrace *stack, bool fatal, uptr tagged_addr, uptr access_size)480      : scoped_report(fatal),481        stack(stack),482        tagged_addr(tagged_addr),483        access_size(access_size),484        untagged_addr(UntagAddr(tagged_addr)),485        ptr_tag(GetTagFromPointer(tagged_addr)),486        mismatch_offset(FindMismatchOffset()),487        heap(CopyHeapChunk()),488        allocations(CopyAllocations()),489        candidate(FindBufferOverflowCandidate()),490        shadow(CopyShadow()) {}491 492 protected:493  struct OverflowCandidate {494    uptr untagged_addr = 0;495    bool after = false;496    bool is_close = false;497 498    struct {499      uptr begin = 0;500      uptr end = 0;501      u32 thread_id = 0;502      u32 stack_id = 0;503      bool is_allocated = false;504    } heap;505  };506 507  struct HeapAllocation {508    HeapAllocationRecord har = {};509    uptr ring_index = 0;510    uptr num_matching_addrs = 0;511    uptr num_matching_addrs_4b = 0;512    u32 free_thread_id = 0;513  };514 515  struct Allocations {516    ArrayRef<SavedStackAllocations> stack;517    ArrayRef<HeapAllocation> heap;518  };519 520  struct HeapChunk {521    uptr begin = 0;522    uptr size = 0;523    u32 stack_id = 0;524    bool from_small_heap = false;525    bool is_allocated = false;526  };527 528  struct Shadow {529    uptr addr = 0;530    tag_t tags[kShadowDumpSize] = {};531    tag_t short_tags[kShortDumpSize] = {};532  };533 534  sptr FindMismatchOffset() const;535  Shadow CopyShadow() const;536  tag_t GetTagCopy(uptr addr) const;537  tag_t GetShortTagCopy(uptr addr) const;538  HeapChunk CopyHeapChunk() const;539  Allocations CopyAllocations();540  OverflowCandidate FindBufferOverflowCandidate() const;541  void PrintAddressDescription() const;542  void PrintHeapOrGlobalCandidate() const;543  void PrintTags(uptr addr) const;544 545  SavedStackAllocations stack_allocations_storage[16];546  HeapAllocation heap_allocations_storage[256];547 548  const ScopedReport scoped_report;549  const StackTrace *stack = nullptr;550  const uptr tagged_addr = 0;551  const uptr access_size = 0;552  const uptr untagged_addr = 0;553  const tag_t ptr_tag = 0;554  const sptr mismatch_offset = 0;555 556  const HeapChunk heap;557  const Allocations allocations;558  const OverflowCandidate candidate;559 560  const Shadow shadow;561};562 563sptr BaseReport::FindMismatchOffset() const {564  if (!access_size)565    return 0;566  sptr offset =567      __hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);568  CHECK_GE(offset, 0);569  CHECK_LT(offset, static_cast<sptr>(access_size));570  tag_t *tag_ptr =571      reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));572  tag_t mem_tag = *tag_ptr;573 574  if (mem_tag && mem_tag < kShadowAlignment) {575    tag_t *granule_ptr = reinterpret_cast<tag_t *>((untagged_addr + offset) &576                                                   ~(kShadowAlignment - 1));577    // If offset is 0, (untagged_addr + offset) is not aligned to granules.578    // This is the offset of the leftmost accessed byte within the bad granule.579    u8 in_granule_offset = (untagged_addr + offset) & (kShadowAlignment - 1);580    tag_t short_tag = granule_ptr[kShadowAlignment - 1];581    // The first mismatch was a short granule that matched the ptr_tag.582    if (short_tag == ptr_tag) {583      // If the access starts after the end of the short granule, then the first584      // bad byte is the first byte of the access; otherwise it is the first585      // byte past the end of the short granule586      if (mem_tag > in_granule_offset) {587        offset += mem_tag - in_granule_offset;588      }589    }590  }591  return offset;592}593 594BaseReport::Shadow BaseReport::CopyShadow() const {595  Shadow result;596  if (!MemIsApp(untagged_addr))597    return result;598 599  result.addr = GetPrintTagStart(untagged_addr + mismatch_offset);600  uptr tag_addr = result.addr;601  uptr short_end = kShortDumpOffset + ARRAY_SIZE(shadow.short_tags);602  for (uptr i = 0; i < ARRAY_SIZE(result.tags); ++i, ++tag_addr) {603    if (!MemIsShadow(tag_addr))604      continue;605    result.tags[i] = *reinterpret_cast<tag_t *>(tag_addr);606    if (i < kShortDumpOffset || i >= short_end)607      continue;608    uptr granule_addr = ShadowToMem(tag_addr);609    if (1 <= result.tags[i] && result.tags[i] <= kShadowAlignment &&610        IsAccessibleMemoryRange(granule_addr, kShadowAlignment)) {611      result.short_tags[i - kShortDumpOffset] =612          *reinterpret_cast<tag_t *>(granule_addr + kShadowAlignment - 1);613    }614  }615  return result;616}617 618tag_t BaseReport::GetTagCopy(uptr addr) const {619  CHECK_GE(addr, shadow.addr);620  uptr idx = addr - shadow.addr;621  CHECK_LT(idx, ARRAY_SIZE(shadow.tags));622  return shadow.tags[idx];623}624 625tag_t BaseReport::GetShortTagCopy(uptr addr) const {626  CHECK_GE(addr, shadow.addr + kShortDumpOffset);627  uptr idx = addr - shadow.addr - kShortDumpOffset;628  CHECK_LT(idx, ARRAY_SIZE(shadow.short_tags));629  return shadow.short_tags[idx];630}631 632BaseReport::HeapChunk BaseReport::CopyHeapChunk() const {633  HeapChunk result = {};634  if (MemIsShadow(untagged_addr))635    return result;636  HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);637  result.begin = chunk.Beg();638  if (result.begin) {639    result.size = chunk.ActualSize();640    result.from_small_heap = chunk.FromSmallHeap();641    result.is_allocated = chunk.IsAllocated();642    result.stack_id = chunk.GetAllocStackId();643  }644  return result;645}646 647BaseReport::Allocations BaseReport::CopyAllocations() {648  if (MemIsShadow(untagged_addr))649    return {};650  uptr stack_allocations_count = 0;651  uptr heap_allocations_count = 0;652  hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {653    if (stack_allocations_count < ARRAY_SIZE(stack_allocations_storage) &&654        t->AddrIsInStack(untagged_addr)) {655      stack_allocations_storage[stack_allocations_count++].CopyFrom(t);656    }657 658    if (heap_allocations_count < ARRAY_SIZE(heap_allocations_storage)) {659      // Scan all threads' ring buffers to find if it's a heap-use-after-free.660      HeapAllocationRecord har;661      uptr ring_index, num_matching_addrs, num_matching_addrs_4b;662      if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,663                             &ring_index, &num_matching_addrs,664                             &num_matching_addrs_4b)) {665        auto &ha = heap_allocations_storage[heap_allocations_count++];666        ha.har = har;667        ha.ring_index = ring_index;668        ha.num_matching_addrs = num_matching_addrs;669        ha.num_matching_addrs_4b = num_matching_addrs_4b;670        ha.free_thread_id = t->unique_id();671      }672    }673  });674 675  return {{stack_allocations_storage, stack_allocations_count},676          {heap_allocations_storage, heap_allocations_count}};677}678 679BaseReport::OverflowCandidate BaseReport::FindBufferOverflowCandidate() const {680  OverflowCandidate result = {};681  if (MemIsShadow(untagged_addr))682    return result;683  // Check if this looks like a heap buffer overflow by scanning684  // the shadow left and right and looking for the first adjacent685  // object with a different memory tag. If that tag matches ptr_tag,686  // check the allocator if it has a live chunk there.687  tag_t *tag_ptr = reinterpret_cast<tag_t *>(MemToShadow(untagged_addr));688  tag_t *candidate_tag_ptr = nullptr, *left = tag_ptr, *right = tag_ptr;689  uptr candidate_distance = 0;690  for (; candidate_distance < 1000; candidate_distance++) {691    if (MemIsShadow(reinterpret_cast<uptr>(left)) && TagsEqual(ptr_tag, left)) {692      candidate_tag_ptr = left;693      break;694    }695    --left;696    if (MemIsShadow(reinterpret_cast<uptr>(right)) &&697        TagsEqual(ptr_tag, right)) {698      candidate_tag_ptr = right;699      break;700    }701    ++right;702  }703 704  constexpr auto kCloseCandidateDistance = 1;705  result.is_close = candidate_distance <= kCloseCandidateDistance;706 707  result.after = candidate_tag_ptr == left;708  result.untagged_addr = ShadowToMem(reinterpret_cast<uptr>(candidate_tag_ptr));709  HwasanChunkView chunk = FindHeapChunkByAddress(result.untagged_addr);710  if (chunk.IsAllocated()) {711    result.heap.is_allocated = true;712    result.heap.begin = chunk.Beg();713    result.heap.end = chunk.End();714    result.heap.thread_id = chunk.GetAllocThreadId();715    result.heap.stack_id = chunk.GetAllocStackId();716  }717  return result;718}719 720void BaseReport::PrintHeapOrGlobalCandidate() const {721  Decorator d;722  if (candidate.heap.is_allocated) {723    uptr offset;724    const char *whence;725    if (candidate.heap.begin <= untagged_addr &&726        untagged_addr < candidate.heap.end) {727      offset = untagged_addr - candidate.heap.begin;728      whence = "inside";729    } else if (candidate.after) {730      offset = untagged_addr - candidate.heap.end;731      whence = "after";732    } else {733      offset = candidate.heap.begin - untagged_addr;734      whence = "before";735    }736    Printf("%s", d.Error());737    Printf("\nCause: heap-buffer-overflow\n");738    Printf("%s", d.Default());739    Printf("%s", d.Location());740    Printf("%p is located %zd bytes %s a %zd-byte region [%p,%p)\n",741           (void*)untagged_addr, offset, whence,742           candidate.heap.end - candidate.heap.begin,743           (void*)candidate.heap.begin, (void*)candidate.heap.end);744    Printf("%s", d.Allocation());745    Printf("allocated by thread T%u here:\n", candidate.heap.thread_id);746    Printf("%s", d.Default());747    GetStackTraceFromId(candidate.heap.stack_id).Print();748    return;749  }750  // Check whether the address points into a loaded library. If so, this is751  // most likely a global variable.752  const char *module_name;753  uptr module_address;754  Symbolizer *sym = Symbolizer::GetOrInit();755  if (sym->GetModuleNameAndOffsetForPC(candidate.untagged_addr, &module_name,756                                       &module_address)) {757    Printf("%s", d.Error());758    Printf("\nCause: global-overflow\n");759    Printf("%s", d.Default());760    DataInfo info;761    Printf("%s", d.Location());762    if (sym->SymbolizeData(candidate.untagged_addr, &info) && info.start) {763      Printf(764          "%p is located %zd bytes %s a %zd-byte global variable "765          "%s [%p,%p) in %s\n",766          (void *)untagged_addr,767          candidate.after ? untagged_addr - (info.start + info.size)768                          : info.start - untagged_addr,769          candidate.after ? "after" : "before", info.size, info.name,770          (void *)info.start, (void *)(info.start + info.size), module_name);771    } else {772      uptr size = GetGlobalSizeFromDescriptor(candidate.untagged_addr);773      if (size == 0)774        // We couldn't find the size of the global from the descriptors.775        Printf(776            "%p is located %s a global variable in "777            "\n    #0 0x%x (%s+0x%x)\n",778            (void*)untagged_addr, candidate.after ? "after" : "before",779            (u32)candidate.untagged_addr, module_name, (u32)module_address);780      else781        Printf(782            "%p is located %s a %zd-byte global variable in "783            "\n    #0 0x%x (%s+0x%x)\n",784            (void*)untagged_addr, candidate.after ? "after" : "before", size,785            (u32)candidate.untagged_addr, module_name, (u32)module_address);786    }787    Printf("%s", d.Default());788  }789}790 791void BaseReport::PrintAddressDescription() const {792  Decorator d;793  int num_descriptions_printed = 0;794 795  if (MemIsShadow(untagged_addr)) {796    Printf("%s%p is HWAsan shadow memory.\n%s", d.Location(),797           (void *)untagged_addr, d.Default());798    return;799  }800 801  // Print some very basic information about the address, if it's a heap.802  if (heap.begin) {803    Printf(804        "%s[%p,%p) is a %s %s heap chunk; "805        "size: %zd offset: %zd\n%s",806        d.Location(), (void *)heap.begin, (void *)(heap.begin + heap.size),807        heap.from_small_heap ? "small" : "large",808        heap.is_allocated ? "allocated" : "unallocated", heap.size,809        untagged_addr - heap.begin, d.Default());810  }811 812  auto announce_by_id = [](u32 thread_id) {813    hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {814      if (thread_id == t->unique_id())815        t->Announce();816    });817  };818 819  // Check stack first. If the address is on the stack of a live thread, we820  // know it cannot be a heap / global overflow.821  for (const auto &sa : allocations.stack) {822    Printf("%s", d.Error());823    Printf("\nCause: stack tag-mismatch\n");824    Printf("%s", d.Location());825    Printf("Address %p is located in stack of thread T%zd\n",826           (void *)untagged_addr, (ssize)sa.thread_id());827    Printf("%s", d.Default());828    announce_by_id(sa.thread_id());829    PrintStackAllocations(sa.get(), ptr_tag, untagged_addr);830    num_descriptions_printed++;831  }832 833  if (allocations.stack.empty() && candidate.untagged_addr &&834      candidate.is_close) {835    PrintHeapOrGlobalCandidate();836    num_descriptions_printed++;837  }838 839  for (const auto &ha : allocations.heap) {840    const HeapAllocationRecord har = ha.har;841 842    Printf("%s", d.Error());843    Printf("\nCause: use-after-free\n");844    Printf("%s", d.Location());845    Printf("%p is located %zd bytes inside a %zd-byte region [%p,%p)\n",846           (void*)untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),847           (ssize)har.requested_size, (void*)UntagAddr(har.tagged_addr),848           (void*)(UntagAddr(har.tagged_addr) + har.requested_size));849    Printf("%s", d.Allocation());850    Printf("freed by thread T%u here:\n", ha.free_thread_id);851    Printf("%s", d.Default());852    GetStackTraceFromId(har.free_context_id).Print();853 854    Printf("%s", d.Allocation());855    Printf("previously allocated by thread T%u here:\n", har.alloc_thread_id);856    Printf("%s", d.Default());857    GetStackTraceFromId(har.alloc_context_id).Print();858 859    // Print a developer note: the index of this heap object860    // in the thread's deallocation ring buffer.861    Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", ha.ring_index + 1,862           (ssize)flags()->heap_history_size);863    Printf("hwasan_dev_note_num_matching_addrs: %zd\n", ha.num_matching_addrs);864    Printf("hwasan_dev_note_num_matching_addrs_4b: %zd\n",865           ha.num_matching_addrs_4b);866 867    announce_by_id(ha.free_thread_id);868    // TODO: announce_by_id(har.alloc_thread_id);869    num_descriptions_printed++;870  }871 872  if (candidate.untagged_addr && num_descriptions_printed == 0) {873    PrintHeapOrGlobalCandidate();874    num_descriptions_printed++;875  }876 877  // Print the remaining threads, as an extra information, 1 line per thread.878  if (flags()->print_live_threads_info) {879    Printf("\n");880    hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });881  }882 883  if (!num_descriptions_printed)884    // We exhausted our possibilities. Bail out.885    Printf("HWAddressSanitizer can not describe address in more detail.\n");886  if (num_descriptions_printed > 1) {887    Printf(888        "There are %d potential causes, printed above in order "889        "of likeliness.\n",890        num_descriptions_printed);891  }892}893 894void BaseReport::PrintTags(uptr addr) const {895  if (shadow.addr) {896    PrintTagsAroundAddr(897        addr, [&](uptr addr) { return GetTagCopy(addr); },898        [&](uptr addr) { return GetShortTagCopy(addr); });899  }900}901 902class InvalidFreeReport : public BaseReport {903 public:904  InvalidFreeReport(StackTrace *stack, uptr tagged_addr)905      : BaseReport(stack, flags()->halt_on_error, tagged_addr, 0) {}906  ~InvalidFreeReport();907 908 private:909};910 911InvalidFreeReport::~InvalidFreeReport() {912  Decorator d;913  Printf("%s", d.Error());914  uptr pc = GetTopPc(stack);915  const char *bug_type = "invalid-free";916  const Thread *thread = GetCurrentThread();917  if (thread) {918    Report("ERROR: %s: %s on address %p at pc %p on thread T%zd\n",919           SanitizerToolName, bug_type, (void *)untagged_addr, (void *)pc,920           (ssize)thread->unique_id());921  } else {922    Report("ERROR: %s: %s on address %p at pc %p on unknown thread\n",923           SanitizerToolName, bug_type, (void *)untagged_addr, (void *)pc);924  }925  Printf("%s", d.Access());926  if (shadow.addr) {927    Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag,928           GetTagCopy(MemToShadow(untagged_addr)));929  }930  Printf("%s", d.Default());931 932  stack->Print();933 934  PrintAddressDescription();935  PrintTags(untagged_addr);936  MaybePrintAndroidHelpUrl();937  ReportErrorSummary(bug_type, stack);938}939 940class TailOverwrittenReport : public BaseReport {941 public:942  explicit TailOverwrittenReport(StackTrace *stack, uptr tagged_addr,943                                 uptr orig_size, const u8 *expected)944      : BaseReport(stack, flags()->halt_on_error, tagged_addr, 0),945        orig_size(orig_size),946        tail_size(kShadowAlignment - (orig_size % kShadowAlignment)) {947    CHECK_GT(tail_size, 0U);948    CHECK_LT(tail_size, kShadowAlignment);949    internal_memcpy(tail_copy,950                    reinterpret_cast<u8 *>(untagged_addr + orig_size),951                    tail_size);952    internal_memcpy(actual_expected, expected, tail_size);953    // Short granule is stashed in the last byte of the magic string. To avoid954    // confusion, make the expected magic string contain the short granule tag.955    if (orig_size % kShadowAlignment != 0)956      actual_expected[tail_size - 1] = ptr_tag;957  }958  ~TailOverwrittenReport();959 960 private:961  const uptr orig_size = 0;962  const uptr tail_size = 0;963  u8 actual_expected[kShadowAlignment] = {};964  u8 tail_copy[kShadowAlignment] = {};965};966 967TailOverwrittenReport::~TailOverwrittenReport() {968  Decorator d;969  Printf("%s", d.Error());970  const char *bug_type = "allocation-tail-overwritten";971  Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,972         bug_type, (void *)untagged_addr, (void *)(untagged_addr + orig_size),973         orig_size);974  Printf("\n%s", d.Default());975  Printf(976      "Stack of invalid access unknown. Issue detected at deallocation "977      "time.\n");978  Printf("%s", d.Allocation());979  Printf("deallocated here:\n");980  Printf("%s", d.Default());981  stack->Print();982  if (heap.begin) {983    Printf("%s", d.Allocation());984    Printf("allocated here:\n");985    Printf("%s", d.Default());986    GetStackTraceFromId(heap.stack_id).Print();987  }988 989  InternalScopedString s;990  u8 *tail = tail_copy;991  s.Append("Tail contains: ");992  for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. ");993  for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", tail[i]);994  s.Append("\n");995  s.Append("Expected:      ");996  for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. ");997  for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", actual_expected[i]);998  s.Append("\n");999  s.Append("               ");1000  for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append("   ");1001  for (uptr i = 0; i < tail_size; i++)1002    s.AppendF("%s ", actual_expected[i] != tail[i] ? "^^" : "  ");1003 1004  s.AppendF(1005      "\nThis error occurs when a buffer overflow overwrites memory\n"1006      "after a heap object, but within the %zd-byte granule, e.g.\n"1007      "   char *x = new char[20];\n"1008      "   x[25] = 42;\n"1009      "%s does not detect such bugs in uninstrumented code at the time of "1010      "write,"1011      "\nbut can detect them at the time of free/delete.\n"1012      "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n",1013      kShadowAlignment, SanitizerToolName);1014  Printf("%s", s.data());1015  GetCurrentThread()->Announce();1016  PrintTags(untagged_addr);1017  MaybePrintAndroidHelpUrl();1018  ReportErrorSummary(bug_type, stack);1019}1020 1021class TagMismatchReport : public BaseReport {1022 public:1023  explicit TagMismatchReport(StackTrace *stack, uptr tagged_addr,1024                             uptr access_size, bool is_store, bool fatal,1025                             uptr *registers_frame)1026      : BaseReport(stack, fatal, tagged_addr, access_size),1027        is_store(is_store),1028        registers_frame(registers_frame) {}1029  ~TagMismatchReport();1030 1031 private:1032  const bool is_store;1033  const uptr *registers_frame;1034};1035 1036TagMismatchReport::~TagMismatchReport() {1037  Decorator d;1038  // TODO: when possible, try to print heap-use-after-free, etc.1039  const char *bug_type = "tag-mismatch";1040  uptr pc = GetTopPc(stack);1041  Printf("%s", d.Error());1042  Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,1043         (void *)untagged_addr, (void *)pc);1044 1045  Thread *t = GetCurrentThread();1046 1047  tag_t mem_tag = GetTagCopy(MemToShadow(untagged_addr + mismatch_offset));1048 1049  Printf("%s", d.Access());1050  if (mem_tag && mem_tag < kShadowAlignment) {1051    tag_t short_tag =1052        GetShortTagCopy(MemToShadow(untagged_addr + mismatch_offset));1053    Printf(1054        "%s of size %zu at %p tags: %02x/%02x(%02x) (ptr/mem) in thread T%zd\n",1055        is_store ? "WRITE" : "READ", access_size, (void *)untagged_addr,1056        ptr_tag, mem_tag, short_tag, (ssize)t->unique_id());1057  } else {1058    Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",1059           is_store ? "WRITE" : "READ", access_size, (void *)untagged_addr,1060           ptr_tag, mem_tag, (ssize)t->unique_id());1061  }1062  if (mismatch_offset)1063    Printf("Invalid access starting at offset %zu\n", mismatch_offset);1064  Printf("%s", d.Default());1065 1066  stack->Print();1067 1068  PrintAddressDescription();1069  t->Announce();1070 1071  PrintTags(untagged_addr + mismatch_offset);1072 1073  if (registers_frame)1074    ReportRegisters(registers_frame, pc);1075 1076  MaybePrintAndroidHelpUrl();1077  ReportErrorSummary(bug_type, stack);1078}1079}  // namespace1080 1081void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {1082  InvalidFreeReport R(stack, tagged_addr);1083}1084 1085void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,1086                           const u8 *expected) {1087  TailOverwrittenReport R(stack, tagged_addr, orig_size, expected);1088}1089 1090void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,1091                       bool is_store, bool fatal, uptr *registers_frame) {1092  TagMismatchReport R(stack, tagged_addr, access_size, is_store, fatal,1093                      registers_frame);1094}1095 1096// See the frame breakdown defined in __hwasan_tag_mismatch (from1097// hwasan_tag_mismatch_{aarch64,riscv64}.S).1098void ReportRegisters(const uptr *frame, uptr pc) {1099  Printf("\nRegisters where the failure occurred (pc %p):\n", (void *)pc);1100 1101  // We explicitly print a single line (4 registers/line) each iteration to1102  // reduce the amount of logcat error messages printed. Each Printf() will1103  // result in a new logcat line, irrespective of whether a newline is present,1104  // and so we wish to reduce the number of Printf() calls we have to make.1105#if defined(__aarch64__)1106  Printf("    x0  %016llx  x1  %016llx  x2  %016llx  x3  %016llx\n",1107       frame[0], frame[1], frame[2], frame[3]);1108#elif SANITIZER_RISCV641109  Printf("    sp  %016llx  x1  %016llx  x2  %016llx  x3  %016llx\n",1110         reinterpret_cast<const u8 *>(frame) + 256, frame[1], frame[2],1111         frame[3]);1112#endif1113  Printf("    x4  %016llx  x5  %016llx  x6  %016llx  x7  %016llx\n",1114       frame[4], frame[5], frame[6], frame[7]);1115  Printf("    x8  %016llx  x9  %016llx  x10 %016llx  x11 %016llx\n",1116       frame[8], frame[9], frame[10], frame[11]);1117  Printf("    x12 %016llx  x13 %016llx  x14 %016llx  x15 %016llx\n",1118       frame[12], frame[13], frame[14], frame[15]);1119  Printf("    x16 %016llx  x17 %016llx  x18 %016llx  x19 %016llx\n",1120       frame[16], frame[17], frame[18], frame[19]);1121  Printf("    x20 %016llx  x21 %016llx  x22 %016llx  x23 %016llx\n",1122       frame[20], frame[21], frame[22], frame[23]);1123  Printf("    x24 %016llx  x25 %016llx  x26 %016llx  x27 %016llx\n",1124       frame[24], frame[25], frame[26], frame[27]);1125  // hwasan_check* reduces the stack pointer by 256, then __hwasan_tag_mismatch1126  // passes it to this function.1127#if defined(__aarch64__)1128  Printf("    x28 %016llx  x29 %016llx  x30 %016llx   sp %016llx\n", frame[28],1129         frame[29], frame[30], reinterpret_cast<const u8 *>(frame) + 256);1130#elif SANITIZER_RISCV641131  Printf("    x28 %016llx  x29 %016llx  x30 %016llx  x31 %016llx\n", frame[28],1132         frame[29], frame[30], frame[31]);1133#else1134#endif1135}1136 1137}  // namespace __hwasan1138 1139void __hwasan_set_error_report_callback(void (*callback)(const char *)) {1140  __hwasan::ScopedReport::SetErrorReportCallback(callback);1141}1142