xref: /freebsd/contrib/llvm-project/compiler-rt/lib/hwasan/hwasan_report.cpp (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
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-exception
6 //
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 
36 using namespace __sanitizer;
37 
38 namespace __hwasan {
39 
40 class ScopedReport {
41  public:
ScopedReport(bool fatal)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 
~ScopedReport()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 
MaybeAppendToErrorMessage(const char * msg)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 
SetErrorReportCallback(void (* callback)(const char *))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 
88 Mutex ScopedReport::error_message_lock_;
89 InternalScopedString *ScopedReport::error_message_ptr_;
90 void (*ScopedReport::error_report_callback_)(const char *);
91 
92 // If there is an active ScopedReport, append to its error message.
AppendToErrorMessageBuffer(const char * buffer)93 void AppendToErrorMessageBuffer(const char *buffer) {
94   ScopedReport::MaybeAppendToErrorMessage(buffer);
95 }
96 
GetStackTraceFromId(u32 id)97 static StackTrace GetStackTraceFromId(u32 id) {
98   CHECK(id);
99   StackTrace res = StackDepotGet(id);
100   CHECK(res.trace);
101   return res;
102 }
103 
MaybePrintAndroidHelpUrl()104 static void MaybePrintAndroidHelpUrl() {
105 #if SANITIZER_ANDROID
106   Printf(
107       "Learn more about HWASan reports: "
108       "https://source.android.com/docs/security/test/memory-safety/"
109       "hwasan-reports\n");
110 #endif
111 }
112 
113 namespace {
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 (for
116 // example, Printf may call syslog() which can itself be built with hwasan).
117 class SavedStackAllocations {
118  public:
119   SavedStackAllocations() = default;
120 
SavedStackAllocations(Thread * t)121   explicit SavedStackAllocations(Thread *t) { CopyFrom(t); }
122 
CopyFrom(Thread * t)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 
~SavedStackAllocations()132   ~SavedStackAllocations() {
133     if (rb_) {
134       StackAllocationsRingBuffer *rb = get();
135       UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
136     }
137   }
138 
get() const139   const StackAllocationsRingBuffer *get() const {
140     return (const StackAllocationsRingBuffer *)&rb_;
141   }
142 
get()143   StackAllocationsRingBuffer *get() {
144     return (StackAllocationsRingBuffer *)&rb_;
145   }
146 
thread_id() const147   u32 thread_id() const { return thread_id_; }
148 
149  private:
150   uptr rb_ = 0;
151   u32 thread_id_;
152 };
153 
154 class Decorator: public __sanitizer::SanitizerCommonDecorator {
155  public:
Decorator()156   Decorator() : SanitizerCommonDecorator() { }
Access()157   const char *Access() { return Blue(); }
Allocation() const158   const char *Allocation() const { return Magenta(); }
Origin() const159   const char *Origin() const { return Magenta(); }
Name() const160   const char *Name() const { return Green(); }
Location()161   const char *Location() { return Green(); }
Thread()162   const char *Thread() { return Green(); }
163 };
164 }  // namespace
165 
FindHeapAllocation(HeapAllocationsRingBuffer * rb,uptr tagged_addr,HeapAllocationRecord * har,uptr * ring_index,uptr * num_matching_addrs,uptr * num_matching_addrs_4b)166 static 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 matched
184     // if we had only one entry per address (e.g. if the ring buffer data was
185     // stored at the address itself). This will help us tune the allocator
186     // 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 matched
193     // 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 
PrintStackAllocations(const StackAllocationsRingBuffer * sa,tag_t addr_tag,uptr untagged_addr)205 static 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 calculate
237       // `local_beg % kRecordFPModulus`.
238       // Out of all possible `local_beg` we will only consider 2 candidates
239       // 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 current
245       // 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 impossible
287       // 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           untagged_addr, offset, whence, local.size, local.name, best_beg,
310           best_beg + local.size, local.function_name, location.data());
311       location.clear();
312       Printf("%s\n", d.Default());
313     }
314     frame.Clear();
315   }
316 
317   if (found_local)
318     return;
319 
320   // We didn't find any locals. Most likely we don't have symbols, so dump
321   // the information that we have for offline analysis.
322   InternalScopedString frame_desc;
323   Printf("Previously allocated frames:\n");
324   for (uptr i = 0; i < frames; i++) {
325     const uptr *record_addr = &(*sa)[i];
326     uptr record = *record_addr;
327     if (!record)
328       break;
329     uptr pc_mask = (1ULL << 48) - 1;
330     uptr pc = record & pc_mask;
331     frame_desc.AppendF("  record_addr:%p record:0x%zx",
332                        reinterpret_cast<const void *>(record_addr), record);
333     SymbolizedStackHolder symbolized_stack(
334         Symbolizer::GetOrInit()->SymbolizePC(pc));
335     const SymbolizedStack *frame = symbolized_stack.get();
336     if (frame) {
337       StackTracePrinter::GetOrInit()->RenderFrame(
338           &frame_desc, " %F %L", 0, frame->info.address, &frame->info,
339           common_flags()->symbolize_vs_style,
340           common_flags()->strip_path_prefix);
341     }
342     Printf("%s\n", frame_desc.data());
343     frame_desc.clear();
344   }
345 }
346 
347 // Returns true if tag == *tag_ptr, reading tags from short granules if
348 // necessary. This may return a false positive if tags 1-15 are used as a
349 // regular tag rather than a short granule marker.
TagsEqual(tag_t tag,tag_t * tag_ptr)350 static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
351   if (tag == *tag_ptr)
352     return true;
353   if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1)
354     return false;
355   uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr));
356   tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1);
357   return tag == inline_tag;
358 }
359 
360 // HWASan globals store the size of the global in the descriptor. In cases where
361 // we don't have a binary with symbols, we can't grab the size of the global
362 // from the debug info - but we might be able to retrieve it from the
363 // descriptor. Returns zero if the lookup failed.
GetGlobalSizeFromDescriptor(uptr ptr)364 static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
365   // Find the ELF object that this global resides in.
366   Dl_info info;
367   if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
368     return 0;
369   auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
370   auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
371       reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);
372 
373   // Get the load bias. This is normally the same as the dli_fbase address on
374   // position-independent code, but can be different on non-PIE executables,
375   // binaries using LLD's partitioning feature, or binaries compiled with a
376   // linker script.
377   ElfW(Addr) load_bias = 0;
378   for (const auto &phdr :
379        ArrayRef<const ElfW(Phdr)>(phdr_begin, phdr_begin + ehdr->e_phnum)) {
380     if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)
381       continue;
382     load_bias = reinterpret_cast<ElfW(Addr)>(ehdr) - phdr.p_vaddr;
383     break;
384   }
385 
386   // Walk all globals in this ELF object, looking for the one we're interested
387   // in. Once we find it, we can stop iterating and return the size of the
388   // global we're interested in.
389   for (const hwasan_global &global :
390        HwasanGlobalsFor(load_bias, phdr_begin, ehdr->e_phnum))
391     if (global.addr() <= ptr && ptr < global.addr() + global.size())
392       return global.size();
393 
394   return 0;
395 }
396 
ReportStats()397 void ReportStats() {}
398 
399 constexpr uptr kDumpWidth = 16;
400 constexpr uptr kShadowLines = 17;
401 constexpr uptr kShadowDumpSize = kShadowLines * kDumpWidth;
402 
403 constexpr uptr kShortLines = 3;
404 constexpr uptr kShortDumpSize = kShortLines * kDumpWidth;
405 constexpr uptr kShortDumpOffset = (kShadowLines - kShortLines) / 2 * kDumpWidth;
406 
GetPrintTagStart(uptr addr)407 static uptr GetPrintTagStart(uptr addr) {
408   addr = MemToShadow(addr);
409   addr = RoundDownTo(addr, kDumpWidth);
410   addr -= kDumpWidth * (kShadowLines / 2);
411   return addr;
412 }
413 
414 template <typename PrintTag>
PrintTagInfoAroundAddr(uptr addr,uptr num_rows,InternalScopedString & s,PrintTag print_tag)415 static void PrintTagInfoAroundAddr(uptr addr, uptr num_rows,
416                                    InternalScopedString &s,
417                                    PrintTag print_tag) {
418   uptr center_row_beg = RoundDownTo(addr, kDumpWidth);
419   uptr beg_row = center_row_beg - kDumpWidth * (num_rows / 2);
420   uptr end_row = center_row_beg + kDumpWidth * ((num_rows + 1) / 2);
421   for (uptr row = beg_row; row < end_row; row += kDumpWidth) {
422     s.Append(row == center_row_beg ? "=>" : "  ");
423     s.AppendF("%p:", (void *)ShadowToMem(row));
424     for (uptr i = 0; i < kDumpWidth; i++) {
425       s.Append(row + i == addr ? "[" : " ");
426       print_tag(s, row + i);
427       s.Append(row + i == addr ? "]" : " ");
428     }
429     s.Append("\n");
430   }
431 }
432 
433 template <typename GetTag, typename GetShortTag>
PrintTagsAroundAddr(uptr addr,GetTag get_tag,GetShortTag get_short_tag)434 static void PrintTagsAroundAddr(uptr addr, GetTag get_tag,
435                                 GetShortTag get_short_tag) {
436   InternalScopedString s;
437   addr = MemToShadow(addr);
438   s.AppendF(
439       "\nMemory tags around the buggy address (one tag corresponds to %zd "
440       "bytes):\n",
441       kShadowAlignment);
442   PrintTagInfoAroundAddr(addr, kShadowLines, s,
443                          [&](InternalScopedString &s, uptr tag_addr) {
444                            tag_t tag = get_tag(tag_addr);
445                            s.AppendF("%02x", tag);
446                          });
447 
448   s.AppendF(
449       "Tags for short granules around the buggy address (one tag corresponds "
450       "to %zd bytes):\n",
451       kShadowAlignment);
452   PrintTagInfoAroundAddr(addr, kShortLines, s,
453                          [&](InternalScopedString &s, uptr tag_addr) {
454                            tag_t tag = get_tag(tag_addr);
455                            if (tag >= 1 && tag <= kShadowAlignment) {
456                              tag_t short_tag = get_short_tag(tag_addr);
457                              s.AppendF("%02x", short_tag);
458                            } else {
459                              s.Append("..");
460                            }
461                          });
462   s.Append(
463       "See "
464       "https://clang.llvm.org/docs/"
465       "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
466       "description of short granule tags\n");
467   Printf("%s", s.data());
468 }
469 
GetTopPc(const StackTrace * stack)470 static uptr GetTopPc(const StackTrace *stack) {
471   return stack->size ? StackTrace::GetPreviousInstructionPc(stack->trace[0])
472                      : 0;
473 }
474 
475 namespace {
476 class BaseReport {
477  public:
BaseReport(StackTrace * stack,bool fatal,uptr tagged_addr,uptr access_size)478   BaseReport(StackTrace *stack, bool fatal, uptr tagged_addr, uptr access_size)
479       : scoped_report(fatal),
480         stack(stack),
481         tagged_addr(tagged_addr),
482         access_size(access_size),
483         untagged_addr(UntagAddr(tagged_addr)),
484         ptr_tag(GetTagFromPointer(tagged_addr)),
485         mismatch_offset(FindMismatchOffset()),
486         heap(CopyHeapChunk()),
487         allocations(CopyAllocations()),
488         candidate(FindBufferOverflowCandidate()),
489         shadow(CopyShadow()) {}
490 
491  protected:
492   struct OverflowCandidate {
493     uptr untagged_addr = 0;
494     bool after = false;
495     bool is_close = false;
496 
497     struct {
498       uptr begin = 0;
499       uptr end = 0;
500       u32 thread_id = 0;
501       u32 stack_id = 0;
502       bool is_allocated = false;
503     } heap;
504   };
505 
506   struct HeapAllocation {
507     HeapAllocationRecord har = {};
508     uptr ring_index = 0;
509     uptr num_matching_addrs = 0;
510     uptr num_matching_addrs_4b = 0;
511     u32 free_thread_id = 0;
512   };
513 
514   struct Allocations {
515     ArrayRef<SavedStackAllocations> stack;
516     ArrayRef<HeapAllocation> heap;
517   };
518 
519   struct HeapChunk {
520     uptr begin = 0;
521     uptr size = 0;
522     u32 stack_id = 0;
523     bool from_small_heap = false;
524     bool is_allocated = false;
525   };
526 
527   struct Shadow {
528     uptr addr = 0;
529     tag_t tags[kShadowDumpSize] = {};
530     tag_t short_tags[kShortDumpSize] = {};
531   };
532 
533   sptr FindMismatchOffset() const;
534   Shadow CopyShadow() const;
535   tag_t GetTagCopy(uptr addr) const;
536   tag_t GetShortTagCopy(uptr addr) const;
537   HeapChunk CopyHeapChunk() const;
538   Allocations CopyAllocations();
539   OverflowCandidate FindBufferOverflowCandidate() const;
540   void PrintAddressDescription() const;
541   void PrintHeapOrGlobalCandidate() const;
542   void PrintTags(uptr addr) const;
543 
544   SavedStackAllocations stack_allocations_storage[16];
545   HeapAllocation heap_allocations_storage[256];
546 
547   const ScopedReport scoped_report;
548   const StackTrace *stack = nullptr;
549   const uptr tagged_addr = 0;
550   const uptr access_size = 0;
551   const uptr untagged_addr = 0;
552   const tag_t ptr_tag = 0;
553   const sptr mismatch_offset = 0;
554 
555   const HeapChunk heap;
556   const Allocations allocations;
557   const OverflowCandidate candidate;
558 
559   const Shadow shadow;
560 };
561 
FindMismatchOffset() const562 sptr BaseReport::FindMismatchOffset() const {
563   if (!access_size)
564     return 0;
565   sptr offset =
566       __hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);
567   CHECK_GE(offset, 0);
568   CHECK_LT(offset, static_cast<sptr>(access_size));
569   tag_t *tag_ptr =
570       reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));
571   tag_t mem_tag = *tag_ptr;
572 
573   if (mem_tag && mem_tag < kShadowAlignment) {
574     tag_t *granule_ptr = reinterpret_cast<tag_t *>((untagged_addr + offset) &
575                                                    ~(kShadowAlignment - 1));
576     // If offset is 0, (untagged_addr + offset) is not aligned to granules.
577     // This is the offset of the leftmost accessed byte within the bad granule.
578     u8 in_granule_offset = (untagged_addr + offset) & (kShadowAlignment - 1);
579     tag_t short_tag = granule_ptr[kShadowAlignment - 1];
580     // The first mismatch was a short granule that matched the ptr_tag.
581     if (short_tag == ptr_tag) {
582       // If the access starts after the end of the short granule, then the first
583       // bad byte is the first byte of the access; otherwise it is the first
584       // byte past the end of the short granule
585       if (mem_tag > in_granule_offset) {
586         offset += mem_tag - in_granule_offset;
587       }
588     }
589   }
590   return offset;
591 }
592 
CopyShadow() const593 BaseReport::Shadow BaseReport::CopyShadow() const {
594   Shadow result;
595   if (!MemIsApp(untagged_addr))
596     return result;
597 
598   result.addr = GetPrintTagStart(untagged_addr + mismatch_offset);
599   uptr tag_addr = result.addr;
600   uptr short_end = kShortDumpOffset + ARRAY_SIZE(shadow.short_tags);
601   for (uptr i = 0; i < ARRAY_SIZE(result.tags); ++i, ++tag_addr) {
602     if (!MemIsShadow(tag_addr))
603       continue;
604     result.tags[i] = *reinterpret_cast<tag_t *>(tag_addr);
605     if (i < kShortDumpOffset || i >= short_end)
606       continue;
607     uptr granule_addr = ShadowToMem(tag_addr);
608     if (1 <= result.tags[i] && result.tags[i] <= kShadowAlignment &&
609         IsAccessibleMemoryRange(granule_addr, kShadowAlignment)) {
610       result.short_tags[i - kShortDumpOffset] =
611           *reinterpret_cast<tag_t *>(granule_addr + kShadowAlignment - 1);
612     }
613   }
614   return result;
615 }
616 
GetTagCopy(uptr addr) const617 tag_t BaseReport::GetTagCopy(uptr addr) const {
618   CHECK_GE(addr, shadow.addr);
619   uptr idx = addr - shadow.addr;
620   CHECK_LT(idx, ARRAY_SIZE(shadow.tags));
621   return shadow.tags[idx];
622 }
623 
GetShortTagCopy(uptr addr) const624 tag_t BaseReport::GetShortTagCopy(uptr addr) const {
625   CHECK_GE(addr, shadow.addr + kShortDumpOffset);
626   uptr idx = addr - shadow.addr - kShortDumpOffset;
627   CHECK_LT(idx, ARRAY_SIZE(shadow.short_tags));
628   return shadow.short_tags[idx];
629 }
630 
CopyHeapChunk() const631 BaseReport::HeapChunk BaseReport::CopyHeapChunk() const {
632   HeapChunk result = {};
633   if (MemIsShadow(untagged_addr))
634     return result;
635   HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
636   result.begin = chunk.Beg();
637   if (result.begin) {
638     result.size = chunk.ActualSize();
639     result.from_small_heap = chunk.FromSmallHeap();
640     result.is_allocated = chunk.IsAllocated();
641     result.stack_id = chunk.GetAllocStackId();
642   }
643   return result;
644 }
645 
CopyAllocations()646 BaseReport::Allocations BaseReport::CopyAllocations() {
647   if (MemIsShadow(untagged_addr))
648     return {};
649   uptr stack_allocations_count = 0;
650   uptr heap_allocations_count = 0;
651   hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
652     if (stack_allocations_count < ARRAY_SIZE(stack_allocations_storage) &&
653         t->AddrIsInStack(untagged_addr)) {
654       stack_allocations_storage[stack_allocations_count++].CopyFrom(t);
655     }
656 
657     if (heap_allocations_count < ARRAY_SIZE(heap_allocations_storage)) {
658       // Scan all threads' ring buffers to find if it's a heap-use-after-free.
659       HeapAllocationRecord har;
660       uptr ring_index, num_matching_addrs, num_matching_addrs_4b;
661       if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,
662                              &ring_index, &num_matching_addrs,
663                              &num_matching_addrs_4b)) {
664         auto &ha = heap_allocations_storage[heap_allocations_count++];
665         ha.har = har;
666         ha.ring_index = ring_index;
667         ha.num_matching_addrs = num_matching_addrs;
668         ha.num_matching_addrs_4b = num_matching_addrs_4b;
669         ha.free_thread_id = t->unique_id();
670       }
671     }
672   });
673 
674   return {{stack_allocations_storage, stack_allocations_count},
675           {heap_allocations_storage, heap_allocations_count}};
676 }
677 
FindBufferOverflowCandidate() const678 BaseReport::OverflowCandidate BaseReport::FindBufferOverflowCandidate() const {
679   OverflowCandidate result = {};
680   if (MemIsShadow(untagged_addr))
681     return result;
682   // Check if this looks like a heap buffer overflow by scanning
683   // the shadow left and right and looking for the first adjacent
684   // object with a different memory tag. If that tag matches ptr_tag,
685   // check the allocator if it has a live chunk there.
686   tag_t *tag_ptr = reinterpret_cast<tag_t *>(MemToShadow(untagged_addr));
687   tag_t *candidate_tag_ptr = nullptr, *left = tag_ptr, *right = tag_ptr;
688   uptr candidate_distance = 0;
689   for (; candidate_distance < 1000; candidate_distance++) {
690     if (MemIsShadow(reinterpret_cast<uptr>(left)) && TagsEqual(ptr_tag, left)) {
691       candidate_tag_ptr = left;
692       break;
693     }
694     --left;
695     if (MemIsShadow(reinterpret_cast<uptr>(right)) &&
696         TagsEqual(ptr_tag, right)) {
697       candidate_tag_ptr = right;
698       break;
699     }
700     ++right;
701   }
702 
703   constexpr auto kCloseCandidateDistance = 1;
704   result.is_close = candidate_distance <= kCloseCandidateDistance;
705 
706   result.after = candidate_tag_ptr == left;
707   result.untagged_addr = ShadowToMem(reinterpret_cast<uptr>(candidate_tag_ptr));
708   HwasanChunkView chunk = FindHeapChunkByAddress(result.untagged_addr);
709   if (chunk.IsAllocated()) {
710     result.heap.is_allocated = true;
711     result.heap.begin = chunk.Beg();
712     result.heap.end = chunk.End();
713     result.heap.thread_id = chunk.GetAllocThreadId();
714     result.heap.stack_id = chunk.GetAllocStackId();
715   }
716   return result;
717 }
718 
PrintHeapOrGlobalCandidate() const719 void BaseReport::PrintHeapOrGlobalCandidate() const {
720   Decorator d;
721   if (candidate.heap.is_allocated) {
722     uptr offset;
723     const char *whence;
724     if (candidate.heap.begin <= untagged_addr &&
725         untagged_addr < candidate.heap.end) {
726       offset = untagged_addr - candidate.heap.begin;
727       whence = "inside";
728     } else if (candidate.after) {
729       offset = untagged_addr - candidate.heap.end;
730       whence = "after";
731     } else {
732       offset = candidate.heap.begin - untagged_addr;
733       whence = "before";
734     }
735     Printf("%s", d.Error());
736     Printf("\nCause: heap-buffer-overflow\n");
737     Printf("%s", d.Default());
738     Printf("%s", d.Location());
739     Printf("%p is located %zd bytes %s a %zd-byte region [%p,%p)\n",
740            untagged_addr, offset, whence,
741            candidate.heap.end - candidate.heap.begin, candidate.heap.begin,
742            candidate.heap.end);
743     Printf("%s", d.Allocation());
744     Printf("allocated by thread T%u here:\n", candidate.heap.thread_id);
745     Printf("%s", d.Default());
746     GetStackTraceFromId(candidate.heap.stack_id).Print();
747     return;
748   }
749   // Check whether the address points into a loaded library. If so, this is
750   // most likely a global variable.
751   const char *module_name;
752   uptr module_address;
753   Symbolizer *sym = Symbolizer::GetOrInit();
754   if (sym->GetModuleNameAndOffsetForPC(candidate.untagged_addr, &module_name,
755                                        &module_address)) {
756     Printf("%s", d.Error());
757     Printf("\nCause: global-overflow\n");
758     Printf("%s", d.Default());
759     DataInfo info;
760     Printf("%s", d.Location());
761     if (sym->SymbolizeData(candidate.untagged_addr, &info) && info.start) {
762       Printf(
763           "%p is located %zd bytes %s a %zd-byte global variable "
764           "%s [%p,%p) in %s\n",
765           untagged_addr,
766           candidate.after ? untagged_addr - (info.start + info.size)
767                           : info.start - untagged_addr,
768           candidate.after ? "after" : "before", info.size, info.name,
769           info.start, info.start + info.size, module_name);
770     } else {
771       uptr size = GetGlobalSizeFromDescriptor(candidate.untagged_addr);
772       if (size == 0)
773         // We couldn't find the size of the global from the descriptors.
774         Printf(
775             "%p is located %s a global variable in "
776             "\n    #0 0x%x (%s+0x%x)\n",
777             untagged_addr, candidate.after ? "after" : "before",
778             candidate.untagged_addr, module_name, module_address);
779       else
780         Printf(
781             "%p is located %s a %zd-byte global variable in "
782             "\n    #0 0x%x (%s+0x%x)\n",
783             untagged_addr, candidate.after ? "after" : "before", size,
784             candidate.untagged_addr, module_name, module_address);
785     }
786     Printf("%s", d.Default());
787   }
788 }
789 
PrintAddressDescription() const790 void BaseReport::PrintAddressDescription() const {
791   Decorator d;
792   int num_descriptions_printed = 0;
793 
794   if (MemIsShadow(untagged_addr)) {
795     Printf("%s%p is HWAsan shadow memory.\n%s", d.Location(), untagged_addr,
796            d.Default());
797     return;
798   }
799 
800   // Print some very basic information about the address, if it's a heap.
801   if (heap.begin) {
802     Printf(
803         "%s[%p,%p) is a %s %s heap chunk; "
804         "size: %zd offset: %zd\n%s",
805         d.Location(), heap.begin, heap.begin + heap.size,
806         heap.from_small_heap ? "small" : "large",
807         heap.is_allocated ? "allocated" : "unallocated", heap.size,
808         untagged_addr - heap.begin, d.Default());
809   }
810 
811   auto announce_by_id = [](u32 thread_id) {
812     hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
813       if (thread_id == t->unique_id())
814         t->Announce();
815     });
816   };
817 
818   // Check stack first. If the address is on the stack of a live thread, we
819   // know it cannot be a heap / global overflow.
820   for (const auto &sa : allocations.stack) {
821     Printf("%s", d.Error());
822     Printf("\nCause: stack tag-mismatch\n");
823     Printf("%s", d.Location());
824     Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
825            sa.thread_id());
826     Printf("%s", d.Default());
827     announce_by_id(sa.thread_id());
828     PrintStackAllocations(sa.get(), ptr_tag, untagged_addr);
829     num_descriptions_printed++;
830   }
831 
832   if (allocations.stack.empty() && candidate.untagged_addr &&
833       candidate.is_close) {
834     PrintHeapOrGlobalCandidate();
835     num_descriptions_printed++;
836   }
837 
838   for (const auto &ha : allocations.heap) {
839     const HeapAllocationRecord har = ha.har;
840 
841     Printf("%s", d.Error());
842     Printf("\nCause: use-after-free\n");
843     Printf("%s", d.Location());
844     Printf("%p is located %zd bytes inside a %zd-byte region [%p,%p)\n",
845            untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
846            har.requested_size, UntagAddr(har.tagged_addr),
847            UntagAddr(har.tagged_addr) + har.requested_size);
848     Printf("%s", d.Allocation());
849     Printf("freed by thread T%u here:\n", ha.free_thread_id);
850     Printf("%s", d.Default());
851     GetStackTraceFromId(har.free_context_id).Print();
852 
853     Printf("%s", d.Allocation());
854     Printf("previously allocated by thread T%u here:\n", har.alloc_thread_id);
855     Printf("%s", d.Default());
856     GetStackTraceFromId(har.alloc_context_id).Print();
857 
858     // Print a developer note: the index of this heap object
859     // in the thread's deallocation ring buffer.
860     Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", ha.ring_index + 1,
861            flags()->heap_history_size);
862     Printf("hwasan_dev_note_num_matching_addrs: %zd\n", ha.num_matching_addrs);
863     Printf("hwasan_dev_note_num_matching_addrs_4b: %zd\n",
864            ha.num_matching_addrs_4b);
865 
866     announce_by_id(ha.free_thread_id);
867     // TODO: announce_by_id(har.alloc_thread_id);
868     num_descriptions_printed++;
869   }
870 
871   if (candidate.untagged_addr && num_descriptions_printed == 0) {
872     PrintHeapOrGlobalCandidate();
873     num_descriptions_printed++;
874   }
875 
876   // Print the remaining threads, as an extra information, 1 line per thread.
877   if (flags()->print_live_threads_info) {
878     Printf("\n");
879     hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
880   }
881 
882   if (!num_descriptions_printed)
883     // We exhausted our possibilities. Bail out.
884     Printf("HWAddressSanitizer can not describe address in more detail.\n");
885   if (num_descriptions_printed > 1) {
886     Printf(
887         "There are %d potential causes, printed above in order "
888         "of likeliness.\n",
889         num_descriptions_printed);
890   }
891 }
892 
PrintTags(uptr addr) const893 void BaseReport::PrintTags(uptr addr) const {
894   if (shadow.addr) {
895     PrintTagsAroundAddr(
896         addr, [&](uptr addr) { return GetTagCopy(addr); },
897         [&](uptr addr) { return GetShortTagCopy(addr); });
898   }
899 }
900 
901 class InvalidFreeReport : public BaseReport {
902  public:
InvalidFreeReport(StackTrace * stack,uptr tagged_addr)903   InvalidFreeReport(StackTrace *stack, uptr tagged_addr)
904       : BaseReport(stack, flags()->halt_on_error, tagged_addr, 0) {}
905   ~InvalidFreeReport();
906 
907  private:
908 };
909 
~InvalidFreeReport()910 InvalidFreeReport::~InvalidFreeReport() {
911   Decorator d;
912   Printf("%s", d.Error());
913   uptr pc = GetTopPc(stack);
914   const char *bug_type = "invalid-free";
915   const Thread *thread = GetCurrentThread();
916   if (thread) {
917     Report("ERROR: %s: %s on address %p at pc %p on thread T%zd\n",
918            SanitizerToolName, bug_type, untagged_addr, pc, thread->unique_id());
919   } else {
920     Report("ERROR: %s: %s on address %p at pc %p on unknown thread\n",
921            SanitizerToolName, bug_type, untagged_addr, pc);
922   }
923   Printf("%s", d.Access());
924   if (shadow.addr) {
925     Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag,
926            GetTagCopy(MemToShadow(untagged_addr)));
927   }
928   Printf("%s", d.Default());
929 
930   stack->Print();
931 
932   PrintAddressDescription();
933   PrintTags(untagged_addr);
934   MaybePrintAndroidHelpUrl();
935   ReportErrorSummary(bug_type, stack);
936 }
937 
938 class TailOverwrittenReport : public BaseReport {
939  public:
TailOverwrittenReport(StackTrace * stack,uptr tagged_addr,uptr orig_size,const u8 * expected)940   explicit TailOverwrittenReport(StackTrace *stack, uptr tagged_addr,
941                                  uptr orig_size, const u8 *expected)
942       : BaseReport(stack, flags()->halt_on_error, tagged_addr, 0),
943         orig_size(orig_size),
944         tail_size(kShadowAlignment - (orig_size % kShadowAlignment)) {
945     CHECK_GT(tail_size, 0U);
946     CHECK_LT(tail_size, kShadowAlignment);
947     internal_memcpy(tail_copy,
948                     reinterpret_cast<u8 *>(untagged_addr + orig_size),
949                     tail_size);
950     internal_memcpy(actual_expected, expected, tail_size);
951     // Short granule is stashed in the last byte of the magic string. To avoid
952     // confusion, make the expected magic string contain the short granule tag.
953     if (orig_size % kShadowAlignment != 0)
954       actual_expected[tail_size - 1] = ptr_tag;
955   }
956   ~TailOverwrittenReport();
957 
958  private:
959   const uptr orig_size = 0;
960   const uptr tail_size = 0;
961   u8 actual_expected[kShadowAlignment] = {};
962   u8 tail_copy[kShadowAlignment] = {};
963 };
964 
~TailOverwrittenReport()965 TailOverwrittenReport::~TailOverwrittenReport() {
966   Decorator d;
967   Printf("%s", d.Error());
968   const char *bug_type = "allocation-tail-overwritten";
969   Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
970          bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
971   Printf("\n%s", d.Default());
972   Printf(
973       "Stack of invalid access unknown. Issue detected at deallocation "
974       "time.\n");
975   Printf("%s", d.Allocation());
976   Printf("deallocated here:\n");
977   Printf("%s", d.Default());
978   stack->Print();
979   if (heap.begin) {
980     Printf("%s", d.Allocation());
981     Printf("allocated here:\n");
982     Printf("%s", d.Default());
983     GetStackTraceFromId(heap.stack_id).Print();
984   }
985 
986   InternalScopedString s;
987   u8 *tail = tail_copy;
988   s.Append("Tail contains: ");
989   for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. ");
990   for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", tail[i]);
991   s.Append("\n");
992   s.Append("Expected:      ");
993   for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append(".. ");
994   for (uptr i = 0; i < tail_size; i++) s.AppendF("%02x ", actual_expected[i]);
995   s.Append("\n");
996   s.Append("               ");
997   for (uptr i = 0; i < kShadowAlignment - tail_size; i++) s.Append("   ");
998   for (uptr i = 0; i < tail_size; i++)
999     s.AppendF("%s ", actual_expected[i] != tail[i] ? "^^" : "  ");
1000 
1001   s.AppendF(
1002       "\nThis error occurs when a buffer overflow overwrites memory\n"
1003       "after a heap object, but within the %zd-byte granule, e.g.\n"
1004       "   char *x = new char[20];\n"
1005       "   x[25] = 42;\n"
1006       "%s does not detect such bugs in uninstrumented code at the time of "
1007       "write,"
1008       "\nbut can detect them at the time of free/delete.\n"
1009       "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n",
1010       kShadowAlignment, SanitizerToolName);
1011   Printf("%s", s.data());
1012   GetCurrentThread()->Announce();
1013   PrintTags(untagged_addr);
1014   MaybePrintAndroidHelpUrl();
1015   ReportErrorSummary(bug_type, stack);
1016 }
1017 
1018 class TagMismatchReport : public BaseReport {
1019  public:
TagMismatchReport(StackTrace * stack,uptr tagged_addr,uptr access_size,bool is_store,bool fatal,uptr * registers_frame)1020   explicit TagMismatchReport(StackTrace *stack, uptr tagged_addr,
1021                              uptr access_size, bool is_store, bool fatal,
1022                              uptr *registers_frame)
1023       : BaseReport(stack, fatal, tagged_addr, access_size),
1024         is_store(is_store),
1025         registers_frame(registers_frame) {}
1026   ~TagMismatchReport();
1027 
1028  private:
1029   const bool is_store;
1030   const uptr *registers_frame;
1031 };
1032 
~TagMismatchReport()1033 TagMismatchReport::~TagMismatchReport() {
1034   Decorator d;
1035   // TODO: when possible, try to print heap-use-after-free, etc.
1036   const char *bug_type = "tag-mismatch";
1037   uptr pc = GetTopPc(stack);
1038   Printf("%s", d.Error());
1039   Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
1040          untagged_addr, pc);
1041 
1042   Thread *t = GetCurrentThread();
1043 
1044   tag_t mem_tag = GetTagCopy(MemToShadow(untagged_addr + mismatch_offset));
1045 
1046   Printf("%s", d.Access());
1047   if (mem_tag && mem_tag < kShadowAlignment) {
1048     tag_t short_tag =
1049         GetShortTagCopy(MemToShadow(untagged_addr + mismatch_offset));
1050     Printf(
1051         "%s of size %zu at %p tags: %02x/%02x(%02x) (ptr/mem) in thread T%zd\n",
1052         is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
1053         mem_tag, short_tag, t->unique_id());
1054   } else {
1055     Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
1056            is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
1057            mem_tag, t->unique_id());
1058   }
1059   if (mismatch_offset)
1060     Printf("Invalid access starting at offset %zu\n", mismatch_offset);
1061   Printf("%s", d.Default());
1062 
1063   stack->Print();
1064 
1065   PrintAddressDescription();
1066   t->Announce();
1067 
1068   PrintTags(untagged_addr + mismatch_offset);
1069 
1070   if (registers_frame)
1071     ReportRegisters(registers_frame, pc);
1072 
1073   MaybePrintAndroidHelpUrl();
1074   ReportErrorSummary(bug_type, stack);
1075 }
1076 }  // namespace
1077 
ReportInvalidFree(StackTrace * stack,uptr tagged_addr)1078 void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
1079   InvalidFreeReport R(stack, tagged_addr);
1080 }
1081 
ReportTailOverwritten(StackTrace * stack,uptr tagged_addr,uptr orig_size,const u8 * expected)1082 void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
1083                            const u8 *expected) {
1084   TailOverwrittenReport R(stack, tagged_addr, orig_size, expected);
1085 }
1086 
ReportTagMismatch(StackTrace * stack,uptr tagged_addr,uptr access_size,bool is_store,bool fatal,uptr * registers_frame)1087 void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
1088                        bool is_store, bool fatal, uptr *registers_frame) {
1089   TagMismatchReport R(stack, tagged_addr, access_size, is_store, fatal,
1090                       registers_frame);
1091 }
1092 
1093 // See the frame breakdown defined in __hwasan_tag_mismatch (from
1094 // hwasan_tag_mismatch_{aarch64,riscv64}.S).
ReportRegisters(const uptr * frame,uptr pc)1095 void ReportRegisters(const uptr *frame, uptr pc) {
1096   Printf("\nRegisters where the failure occurred (pc %p):\n", pc);
1097 
1098   // We explicitly print a single line (4 registers/line) each iteration to
1099   // reduce the amount of logcat error messages printed. Each Printf() will
1100   // result in a new logcat line, irrespective of whether a newline is present,
1101   // and so we wish to reduce the number of Printf() calls we have to make.
1102 #if defined(__aarch64__)
1103   Printf("    x0  %016llx  x1  %016llx  x2  %016llx  x3  %016llx\n",
1104        frame[0], frame[1], frame[2], frame[3]);
1105 #elif SANITIZER_RISCV64
1106   Printf("    sp  %016llx  x1  %016llx  x2  %016llx  x3  %016llx\n",
1107          reinterpret_cast<const u8 *>(frame) + 256, frame[1], frame[2],
1108          frame[3]);
1109 #endif
1110   Printf("    x4  %016llx  x5  %016llx  x6  %016llx  x7  %016llx\n",
1111        frame[4], frame[5], frame[6], frame[7]);
1112   Printf("    x8  %016llx  x9  %016llx  x10 %016llx  x11 %016llx\n",
1113        frame[8], frame[9], frame[10], frame[11]);
1114   Printf("    x12 %016llx  x13 %016llx  x14 %016llx  x15 %016llx\n",
1115        frame[12], frame[13], frame[14], frame[15]);
1116   Printf("    x16 %016llx  x17 %016llx  x18 %016llx  x19 %016llx\n",
1117        frame[16], frame[17], frame[18], frame[19]);
1118   Printf("    x20 %016llx  x21 %016llx  x22 %016llx  x23 %016llx\n",
1119        frame[20], frame[21], frame[22], frame[23]);
1120   Printf("    x24 %016llx  x25 %016llx  x26 %016llx  x27 %016llx\n",
1121        frame[24], frame[25], frame[26], frame[27]);
1122   // hwasan_check* reduces the stack pointer by 256, then __hwasan_tag_mismatch
1123   // passes it to this function.
1124 #if defined(__aarch64__)
1125   Printf("    x28 %016llx  x29 %016llx  x30 %016llx   sp %016llx\n", frame[28],
1126          frame[29], frame[30], reinterpret_cast<const u8 *>(frame) + 256);
1127 #elif SANITIZER_RISCV64
1128   Printf("    x28 %016llx  x29 %016llx  x30 %016llx  x31 %016llx\n", frame[28],
1129          frame[29], frame[30], frame[31]);
1130 #else
1131 #endif
1132 }
1133 
1134 }  // namespace __hwasan
1135 
__hwasan_set_error_report_callback(void (* callback)(const char *))1136 void __hwasan_set_error_report_callback(void (*callback)(const char *)) {
1137   __hwasan::ScopedReport::SetErrorReportCallback(callback);
1138 }
1139