13cab2bb3Spatrick //===-- hwasan_report.cpp -------------------------------------------------===//
23cab2bb3Spatrick //
33cab2bb3Spatrick // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
43cab2bb3Spatrick // See https://llvm.org/LICENSE.txt for license information.
53cab2bb3Spatrick // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
63cab2bb3Spatrick //
73cab2bb3Spatrick //===----------------------------------------------------------------------===//
83cab2bb3Spatrick //
93cab2bb3Spatrick // This file is a part of HWAddressSanitizer.
103cab2bb3Spatrick //
113cab2bb3Spatrick // Error reporting.
123cab2bb3Spatrick //===----------------------------------------------------------------------===//
133cab2bb3Spatrick
141f9cb04fSpatrick #include "hwasan_report.h"
151f9cb04fSpatrick
161f9cb04fSpatrick #include <dlfcn.h>
171f9cb04fSpatrick
183cab2bb3Spatrick #include "hwasan.h"
193cab2bb3Spatrick #include "hwasan_allocator.h"
201f9cb04fSpatrick #include "hwasan_globals.h"
213cab2bb3Spatrick #include "hwasan_mapping.h"
223cab2bb3Spatrick #include "hwasan_thread.h"
233cab2bb3Spatrick #include "hwasan_thread_list.h"
243cab2bb3Spatrick #include "sanitizer_common/sanitizer_allocator_internal.h"
253cab2bb3Spatrick #include "sanitizer_common/sanitizer_common.h"
263cab2bb3Spatrick #include "sanitizer_common/sanitizer_flags.h"
273cab2bb3Spatrick #include "sanitizer_common/sanitizer_mutex.h"
283cab2bb3Spatrick #include "sanitizer_common/sanitizer_report_decorator.h"
293cab2bb3Spatrick #include "sanitizer_common/sanitizer_stackdepot.h"
303cab2bb3Spatrick #include "sanitizer_common/sanitizer_stacktrace_printer.h"
313cab2bb3Spatrick #include "sanitizer_common/sanitizer_symbolizer.h"
323cab2bb3Spatrick
333cab2bb3Spatrick using namespace __sanitizer;
343cab2bb3Spatrick
353cab2bb3Spatrick namespace __hwasan {
363cab2bb3Spatrick
373cab2bb3Spatrick class ScopedReport {
383cab2bb3Spatrick public:
ScopedReport(bool fatal=false)393cab2bb3Spatrick ScopedReport(bool fatal = false) : error_message_(1), fatal(fatal) {
40*810390e3Srobert Lock lock(&error_message_lock_);
413cab2bb3Spatrick error_message_ptr_ = fatal ? &error_message_ : nullptr;
423cab2bb3Spatrick ++hwasan_report_count;
433cab2bb3Spatrick }
443cab2bb3Spatrick
~ScopedReport()453cab2bb3Spatrick ~ScopedReport() {
46d89ec533Spatrick void (*report_cb)(const char *);
473cab2bb3Spatrick {
48*810390e3Srobert Lock lock(&error_message_lock_);
49d89ec533Spatrick report_cb = error_report_callback_;
503cab2bb3Spatrick error_message_ptr_ = nullptr;
513cab2bb3Spatrick }
52d89ec533Spatrick if (report_cb)
53d89ec533Spatrick report_cb(error_message_.data());
54d89ec533Spatrick if (fatal)
55d89ec533Spatrick SetAbortMessage(error_message_.data());
563cab2bb3Spatrick if (common_flags()->print_module_map >= 2 ||
573cab2bb3Spatrick (fatal && common_flags()->print_module_map))
583cab2bb3Spatrick DumpProcessMap();
593cab2bb3Spatrick if (fatal)
603cab2bb3Spatrick Die();
613cab2bb3Spatrick }
623cab2bb3Spatrick
MaybeAppendToErrorMessage(const char * msg)633cab2bb3Spatrick static void MaybeAppendToErrorMessage(const char *msg) {
64*810390e3Srobert Lock lock(&error_message_lock_);
653cab2bb3Spatrick if (!error_message_ptr_)
663cab2bb3Spatrick return;
673cab2bb3Spatrick uptr len = internal_strlen(msg);
683cab2bb3Spatrick uptr old_size = error_message_ptr_->size();
693cab2bb3Spatrick error_message_ptr_->resize(old_size + len);
703cab2bb3Spatrick // overwrite old trailing '\0', keep new trailing '\0' untouched.
713cab2bb3Spatrick internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len);
723cab2bb3Spatrick }
73d89ec533Spatrick
SetErrorReportCallback(void (* callback)(const char *))74d89ec533Spatrick static void SetErrorReportCallback(void (*callback)(const char *)) {
75*810390e3Srobert Lock lock(&error_message_lock_);
76d89ec533Spatrick error_report_callback_ = callback;
77d89ec533Spatrick }
78d89ec533Spatrick
793cab2bb3Spatrick private:
803cab2bb3Spatrick ScopedErrorReportLock error_report_lock_;
813cab2bb3Spatrick InternalMmapVector<char> error_message_;
823cab2bb3Spatrick bool fatal;
833cab2bb3Spatrick
843cab2bb3Spatrick static InternalMmapVector<char> *error_message_ptr_;
85*810390e3Srobert static Mutex error_message_lock_;
86d89ec533Spatrick static void (*error_report_callback_)(const char *);
873cab2bb3Spatrick };
883cab2bb3Spatrick
893cab2bb3Spatrick InternalMmapVector<char> *ScopedReport::error_message_ptr_;
90*810390e3Srobert Mutex ScopedReport::error_message_lock_;
91d89ec533Spatrick void (*ScopedReport::error_report_callback_)(const char *);
923cab2bb3Spatrick
933cab2bb3Spatrick // If there is an active ScopedReport, append to its error message.
AppendToErrorMessageBuffer(const char * buffer)943cab2bb3Spatrick void AppendToErrorMessageBuffer(const char *buffer) {
953cab2bb3Spatrick ScopedReport::MaybeAppendToErrorMessage(buffer);
963cab2bb3Spatrick }
973cab2bb3Spatrick
GetStackTraceFromId(u32 id)983cab2bb3Spatrick static StackTrace GetStackTraceFromId(u32 id) {
993cab2bb3Spatrick CHECK(id);
1003cab2bb3Spatrick StackTrace res = StackDepotGet(id);
1013cab2bb3Spatrick CHECK(res.trace);
1023cab2bb3Spatrick return res;
1033cab2bb3Spatrick }
1043cab2bb3Spatrick
MaybePrintAndroidHelpUrl()105*810390e3Srobert static void MaybePrintAndroidHelpUrl() {
106*810390e3Srobert #if SANITIZER_ANDROID
107*810390e3Srobert Printf(
108*810390e3Srobert "Learn more about HWASan reports: "
109*810390e3Srobert "https://source.android.com/docs/security/test/memory-safety/"
110*810390e3Srobert "hwasan-reports\n");
111*810390e3Srobert #endif
112*810390e3Srobert }
113*810390e3Srobert
1143cab2bb3Spatrick // A RAII object that holds a copy of the current thread stack ring buffer.
1153cab2bb3Spatrick // The actual stack buffer may change while we are iterating over it (for
1163cab2bb3Spatrick // example, Printf may call syslog() which can itself be built with hwasan).
1173cab2bb3Spatrick class SavedStackAllocations {
1183cab2bb3Spatrick public:
SavedStackAllocations(StackAllocationsRingBuffer * rb)1193cab2bb3Spatrick SavedStackAllocations(StackAllocationsRingBuffer *rb) {
1203cab2bb3Spatrick uptr size = rb->size() * sizeof(uptr);
1213cab2bb3Spatrick void *storage =
1223cab2bb3Spatrick MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations");
1233cab2bb3Spatrick new (&rb_) StackAllocationsRingBuffer(*rb, storage);
1243cab2bb3Spatrick }
1253cab2bb3Spatrick
~SavedStackAllocations()1263cab2bb3Spatrick ~SavedStackAllocations() {
1273cab2bb3Spatrick StackAllocationsRingBuffer *rb = get();
1283cab2bb3Spatrick UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr));
1293cab2bb3Spatrick }
1303cab2bb3Spatrick
get()1313cab2bb3Spatrick StackAllocationsRingBuffer *get() {
1323cab2bb3Spatrick return (StackAllocationsRingBuffer *)&rb_;
1333cab2bb3Spatrick }
1343cab2bb3Spatrick
1353cab2bb3Spatrick private:
1363cab2bb3Spatrick uptr rb_;
1373cab2bb3Spatrick };
1383cab2bb3Spatrick
1393cab2bb3Spatrick class Decorator: public __sanitizer::SanitizerCommonDecorator {
1403cab2bb3Spatrick public:
Decorator()1413cab2bb3Spatrick Decorator() : SanitizerCommonDecorator() { }
Access()1423cab2bb3Spatrick const char *Access() { return Blue(); }
Allocation() const1433cab2bb3Spatrick const char *Allocation() const { return Magenta(); }
Origin() const1443cab2bb3Spatrick const char *Origin() const { return Magenta(); }
Name() const1453cab2bb3Spatrick const char *Name() const { return Green(); }
Location()1463cab2bb3Spatrick const char *Location() { return Green(); }
Thread()1473cab2bb3Spatrick const char *Thread() { return Green(); }
1483cab2bb3Spatrick };
1493cab2bb3Spatrick
FindHeapAllocation(HeapAllocationsRingBuffer * rb,uptr tagged_addr,HeapAllocationRecord * har,uptr * ring_index,uptr * num_matching_addrs,uptr * num_matching_addrs_4b)1501f9cb04fSpatrick static bool FindHeapAllocation(HeapAllocationsRingBuffer *rb, uptr tagged_addr,
1511f9cb04fSpatrick HeapAllocationRecord *har, uptr *ring_index,
1521f9cb04fSpatrick uptr *num_matching_addrs,
1531f9cb04fSpatrick uptr *num_matching_addrs_4b) {
1541f9cb04fSpatrick if (!rb) return false;
1551f9cb04fSpatrick
1561f9cb04fSpatrick *num_matching_addrs = 0;
1571f9cb04fSpatrick *num_matching_addrs_4b = 0;
1583cab2bb3Spatrick for (uptr i = 0, size = rb->size(); i < size; i++) {
1593cab2bb3Spatrick auto h = (*rb)[i];
1603cab2bb3Spatrick if (h.tagged_addr <= tagged_addr &&
1613cab2bb3Spatrick h.tagged_addr + h.requested_size > tagged_addr) {
1623cab2bb3Spatrick *har = h;
1631f9cb04fSpatrick *ring_index = i;
1641f9cb04fSpatrick return true;
1651f9cb04fSpatrick }
1661f9cb04fSpatrick
1671f9cb04fSpatrick // Measure the number of heap ring buffer entries that would have matched
1681f9cb04fSpatrick // if we had only one entry per address (e.g. if the ring buffer data was
1691f9cb04fSpatrick // stored at the address itself). This will help us tune the allocator
1701f9cb04fSpatrick // implementation for MTE.
1711f9cb04fSpatrick if (UntagAddr(h.tagged_addr) <= UntagAddr(tagged_addr) &&
1721f9cb04fSpatrick UntagAddr(h.tagged_addr) + h.requested_size > UntagAddr(tagged_addr)) {
1731f9cb04fSpatrick ++*num_matching_addrs;
1741f9cb04fSpatrick }
1751f9cb04fSpatrick
1761f9cb04fSpatrick // Measure the number of heap ring buffer entries that would have matched
1771f9cb04fSpatrick // if we only had 4 tag bits, which is the case for MTE.
1781f9cb04fSpatrick auto untag_4b = [](uptr p) {
1791f9cb04fSpatrick return p & ((1ULL << 60) - 1);
1801f9cb04fSpatrick };
1811f9cb04fSpatrick if (untag_4b(h.tagged_addr) <= untag_4b(tagged_addr) &&
1821f9cb04fSpatrick untag_4b(h.tagged_addr) + h.requested_size > untag_4b(tagged_addr)) {
1831f9cb04fSpatrick ++*num_matching_addrs_4b;
1843cab2bb3Spatrick }
1853cab2bb3Spatrick }
1861f9cb04fSpatrick return false;
1873cab2bb3Spatrick }
1883cab2bb3Spatrick
PrintStackAllocations(StackAllocationsRingBuffer * sa,tag_t addr_tag,uptr untagged_addr)1893cab2bb3Spatrick static void PrintStackAllocations(StackAllocationsRingBuffer *sa,
1903cab2bb3Spatrick tag_t addr_tag, uptr untagged_addr) {
1913cab2bb3Spatrick uptr frames = Min((uptr)flags()->stack_history_size, sa->size());
1923cab2bb3Spatrick bool found_local = false;
1933cab2bb3Spatrick for (uptr i = 0; i < frames; i++) {
1943cab2bb3Spatrick const uptr *record_addr = &(*sa)[i];
1953cab2bb3Spatrick uptr record = *record_addr;
1963cab2bb3Spatrick if (!record)
1973cab2bb3Spatrick break;
1983cab2bb3Spatrick tag_t base_tag =
1993cab2bb3Spatrick reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift;
2003cab2bb3Spatrick uptr fp = (record >> kRecordFPShift) << kRecordFPLShift;
2013cab2bb3Spatrick uptr pc_mask = (1ULL << kRecordFPShift) - 1;
2023cab2bb3Spatrick uptr pc = record & pc_mask;
2033cab2bb3Spatrick FrameInfo frame;
2043cab2bb3Spatrick if (Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) {
2053cab2bb3Spatrick for (LocalInfo &local : frame.locals) {
2063cab2bb3Spatrick if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset)
2073cab2bb3Spatrick continue;
2083cab2bb3Spatrick tag_t obj_tag = base_tag ^ local.tag_offset;
2093cab2bb3Spatrick if (obj_tag != addr_tag)
2103cab2bb3Spatrick continue;
2113cab2bb3Spatrick // Calculate the offset from the object address to the faulting
2123cab2bb3Spatrick // address. Because we only store bits 4-19 of FP (bits 0-3 are
2133cab2bb3Spatrick // guaranteed to be zero), the calculation is performed mod 2^20 and may
2143cab2bb3Spatrick // harmlessly underflow if the address mod 2^20 is below the object
2153cab2bb3Spatrick // address.
2163cab2bb3Spatrick uptr obj_offset =
2173cab2bb3Spatrick (untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1);
2183cab2bb3Spatrick if (obj_offset >= local.size)
2193cab2bb3Spatrick continue;
2203cab2bb3Spatrick if (!found_local) {
2213cab2bb3Spatrick Printf("Potentially referenced stack objects:\n");
2223cab2bb3Spatrick found_local = true;
2233cab2bb3Spatrick }
2243cab2bb3Spatrick Printf(" %s in %s %s:%d\n", local.name, local.function_name,
2253cab2bb3Spatrick local.decl_file, local.decl_line);
2263cab2bb3Spatrick }
2273cab2bb3Spatrick frame.Clear();
2283cab2bb3Spatrick }
2293cab2bb3Spatrick }
2303cab2bb3Spatrick
2313cab2bb3Spatrick if (found_local)
2323cab2bb3Spatrick return;
2333cab2bb3Spatrick
2343cab2bb3Spatrick // We didn't find any locals. Most likely we don't have symbols, so dump
2353cab2bb3Spatrick // the information that we have for offline analysis.
236d89ec533Spatrick InternalScopedString frame_desc;
2373cab2bb3Spatrick Printf("Previously allocated frames:\n");
2383cab2bb3Spatrick for (uptr i = 0; i < frames; i++) {
2393cab2bb3Spatrick const uptr *record_addr = &(*sa)[i];
2403cab2bb3Spatrick uptr record = *record_addr;
2413cab2bb3Spatrick if (!record)
2423cab2bb3Spatrick break;
2433cab2bb3Spatrick uptr pc_mask = (1ULL << 48) - 1;
2443cab2bb3Spatrick uptr pc = record & pc_mask;
2453cab2bb3Spatrick frame_desc.append(" record_addr:0x%zx record:0x%zx",
2463cab2bb3Spatrick reinterpret_cast<uptr>(record_addr), record);
2473cab2bb3Spatrick if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) {
248d89ec533Spatrick RenderFrame(&frame_desc, " %F %L", 0, frame->info.address, &frame->info,
2493cab2bb3Spatrick common_flags()->symbolize_vs_style,
2503cab2bb3Spatrick common_flags()->strip_path_prefix);
2513cab2bb3Spatrick frame->ClearAll();
2523cab2bb3Spatrick }
253d89ec533Spatrick Printf("%s\n", frame_desc.data());
2543cab2bb3Spatrick frame_desc.clear();
2553cab2bb3Spatrick }
2563cab2bb3Spatrick }
2573cab2bb3Spatrick
2583cab2bb3Spatrick // Returns true if tag == *tag_ptr, reading tags from short granules if
2593cab2bb3Spatrick // necessary. This may return a false positive if tags 1-15 are used as a
2603cab2bb3Spatrick // regular tag rather than a short granule marker.
TagsEqual(tag_t tag,tag_t * tag_ptr)2613cab2bb3Spatrick static bool TagsEqual(tag_t tag, tag_t *tag_ptr) {
2623cab2bb3Spatrick if (tag == *tag_ptr)
2633cab2bb3Spatrick return true;
2643cab2bb3Spatrick if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1)
2653cab2bb3Spatrick return false;
2663cab2bb3Spatrick uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr));
2673cab2bb3Spatrick tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1);
2683cab2bb3Spatrick return tag == inline_tag;
2693cab2bb3Spatrick }
2703cab2bb3Spatrick
2711f9cb04fSpatrick // HWASan globals store the size of the global in the descriptor. In cases where
2721f9cb04fSpatrick // we don't have a binary with symbols, we can't grab the size of the global
2731f9cb04fSpatrick // from the debug info - but we might be able to retrieve it from the
2741f9cb04fSpatrick // descriptor. Returns zero if the lookup failed.
GetGlobalSizeFromDescriptor(uptr ptr)2751f9cb04fSpatrick static uptr GetGlobalSizeFromDescriptor(uptr ptr) {
2761f9cb04fSpatrick // Find the ELF object that this global resides in.
2771f9cb04fSpatrick Dl_info info;
278d89ec533Spatrick if (dladdr(reinterpret_cast<void *>(ptr), &info) == 0)
279d89ec533Spatrick return 0;
2801f9cb04fSpatrick auto *ehdr = reinterpret_cast<const ElfW(Ehdr) *>(info.dli_fbase);
2811f9cb04fSpatrick auto *phdr_begin = reinterpret_cast<const ElfW(Phdr) *>(
2821f9cb04fSpatrick reinterpret_cast<const u8 *>(ehdr) + ehdr->e_phoff);
2831f9cb04fSpatrick
2841f9cb04fSpatrick // Get the load bias. This is normally the same as the dli_fbase address on
2851f9cb04fSpatrick // position-independent code, but can be different on non-PIE executables,
2861f9cb04fSpatrick // binaries using LLD's partitioning feature, or binaries compiled with a
2871f9cb04fSpatrick // linker script.
2881f9cb04fSpatrick ElfW(Addr) load_bias = 0;
2891f9cb04fSpatrick for (const auto &phdr :
2901f9cb04fSpatrick ArrayRef<const ElfW(Phdr)>(phdr_begin, phdr_begin + ehdr->e_phnum)) {
2911f9cb04fSpatrick if (phdr.p_type != PT_LOAD || phdr.p_offset != 0)
2921f9cb04fSpatrick continue;
2931f9cb04fSpatrick load_bias = reinterpret_cast<ElfW(Addr)>(ehdr) - phdr.p_vaddr;
2941f9cb04fSpatrick break;
2951f9cb04fSpatrick }
2961f9cb04fSpatrick
2971f9cb04fSpatrick // Walk all globals in this ELF object, looking for the one we're interested
2981f9cb04fSpatrick // in. Once we find it, we can stop iterating and return the size of the
2991f9cb04fSpatrick // global we're interested in.
3001f9cb04fSpatrick for (const hwasan_global &global :
3011f9cb04fSpatrick HwasanGlobalsFor(load_bias, phdr_begin, ehdr->e_phnum))
3021f9cb04fSpatrick if (global.addr() <= ptr && ptr < global.addr() + global.size())
3031f9cb04fSpatrick return global.size();
3041f9cb04fSpatrick
3051f9cb04fSpatrick return 0;
3061f9cb04fSpatrick }
3071f9cb04fSpatrick
ShowHeapOrGlobalCandidate(uptr untagged_addr,tag_t * candidate,tag_t * left,tag_t * right)308d89ec533Spatrick static void ShowHeapOrGlobalCandidate(uptr untagged_addr, tag_t *candidate,
309d89ec533Spatrick tag_t *left, tag_t *right) {
310d89ec533Spatrick Decorator d;
311d89ec533Spatrick uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate));
312d89ec533Spatrick HwasanChunkView chunk = FindHeapChunkByAddress(mem);
313d89ec533Spatrick if (chunk.IsAllocated()) {
314d89ec533Spatrick uptr offset;
315d89ec533Spatrick const char *whence;
316d89ec533Spatrick if (untagged_addr < chunk.End() && untagged_addr >= chunk.Beg()) {
317d89ec533Spatrick offset = untagged_addr - chunk.Beg();
318d89ec533Spatrick whence = "inside";
319d89ec533Spatrick } else if (candidate == left) {
320d89ec533Spatrick offset = untagged_addr - chunk.End();
321*810390e3Srobert whence = "after";
322d89ec533Spatrick } else {
323d89ec533Spatrick offset = chunk.Beg() - untagged_addr;
324*810390e3Srobert whence = "before";
325d89ec533Spatrick }
326d89ec533Spatrick Printf("%s", d.Error());
327d89ec533Spatrick Printf("\nCause: heap-buffer-overflow\n");
328d89ec533Spatrick Printf("%s", d.Default());
329d89ec533Spatrick Printf("%s", d.Location());
330*810390e3Srobert Printf("%p is located %zd bytes %s a %zd-byte region [%p,%p)\n",
331d89ec533Spatrick untagged_addr, offset, whence, chunk.UsedSize(), chunk.Beg(),
332d89ec533Spatrick chunk.End());
333d89ec533Spatrick Printf("%s", d.Allocation());
334d89ec533Spatrick Printf("allocated here:\n");
335d89ec533Spatrick Printf("%s", d.Default());
336d89ec533Spatrick GetStackTraceFromId(chunk.GetAllocStackId()).Print();
337d89ec533Spatrick return;
338d89ec533Spatrick }
339d89ec533Spatrick // Check whether the address points into a loaded library. If so, this is
340d89ec533Spatrick // most likely a global variable.
341d89ec533Spatrick const char *module_name;
342d89ec533Spatrick uptr module_address;
343d89ec533Spatrick Symbolizer *sym = Symbolizer::GetOrInit();
344d89ec533Spatrick if (sym->GetModuleNameAndOffsetForPC(mem, &module_name, &module_address)) {
345d89ec533Spatrick Printf("%s", d.Error());
346d89ec533Spatrick Printf("\nCause: global-overflow\n");
347d89ec533Spatrick Printf("%s", d.Default());
348d89ec533Spatrick DataInfo info;
349d89ec533Spatrick Printf("%s", d.Location());
350d89ec533Spatrick if (sym->SymbolizeData(mem, &info) && info.start) {
351d89ec533Spatrick Printf(
352*810390e3Srobert "%p is located %zd bytes %s a %zd-byte global variable "
353d89ec533Spatrick "%s [%p,%p) in %s\n",
354d89ec533Spatrick untagged_addr,
355d89ec533Spatrick candidate == left ? untagged_addr - (info.start + info.size)
356d89ec533Spatrick : info.start - untagged_addr,
357*810390e3Srobert candidate == left ? "after" : "before", info.size, info.name,
358d89ec533Spatrick info.start, info.start + info.size, module_name);
359d89ec533Spatrick } else {
360d89ec533Spatrick uptr size = GetGlobalSizeFromDescriptor(mem);
361d89ec533Spatrick if (size == 0)
362d89ec533Spatrick // We couldn't find the size of the global from the descriptors.
363*810390e3Srobert Printf(
364*810390e3Srobert "%p is located %s a global variable in "
365*810390e3Srobert "\n #0 0x%x (%s+0x%x)\n",
366*810390e3Srobert untagged_addr, candidate == left ? "after" : "before", mem,
367*810390e3Srobert module_name, module_address);
368d89ec533Spatrick else
369d89ec533Spatrick Printf(
370*810390e3Srobert "%p is located %s a %zd-byte global variable in "
371*810390e3Srobert "\n #0 0x%x (%s+0x%x)\n",
372*810390e3Srobert untagged_addr, candidate == left ? "after" : "before", size, mem,
373d89ec533Spatrick module_name, module_address);
374d89ec533Spatrick }
375d89ec533Spatrick Printf("%s", d.Default());
376d89ec533Spatrick }
377d89ec533Spatrick }
378d89ec533Spatrick
PrintAddressDescription(uptr tagged_addr,uptr access_size,StackAllocationsRingBuffer * current_stack_allocations)3793cab2bb3Spatrick void PrintAddressDescription(
3803cab2bb3Spatrick uptr tagged_addr, uptr access_size,
3813cab2bb3Spatrick StackAllocationsRingBuffer *current_stack_allocations) {
3823cab2bb3Spatrick Decorator d;
3833cab2bb3Spatrick int num_descriptions_printed = 0;
3843cab2bb3Spatrick uptr untagged_addr = UntagAddr(tagged_addr);
3853cab2bb3Spatrick
386*810390e3Srobert if (MemIsShadow(untagged_addr)) {
387*810390e3Srobert Printf("%s%p is HWAsan shadow memory.\n%s", d.Location(), untagged_addr,
388*810390e3Srobert d.Default());
389*810390e3Srobert return;
390*810390e3Srobert }
391*810390e3Srobert
3923cab2bb3Spatrick // Print some very basic information about the address, if it's a heap.
3933cab2bb3Spatrick HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
3943cab2bb3Spatrick if (uptr beg = chunk.Beg()) {
3953cab2bb3Spatrick uptr size = chunk.ActualSize();
3963cab2bb3Spatrick Printf("%s[%p,%p) is a %s %s heap chunk; "
3973cab2bb3Spatrick "size: %zd offset: %zd\n%s",
3983cab2bb3Spatrick d.Location(),
3993cab2bb3Spatrick beg, beg + size,
4003cab2bb3Spatrick chunk.FromSmallHeap() ? "small" : "large",
4013cab2bb3Spatrick chunk.IsAllocated() ? "allocated" : "unallocated",
4023cab2bb3Spatrick size, untagged_addr - beg,
4033cab2bb3Spatrick d.Default());
4043cab2bb3Spatrick }
4053cab2bb3Spatrick
406d89ec533Spatrick tag_t addr_tag = GetTagFromPointer(tagged_addr);
407d89ec533Spatrick
408d89ec533Spatrick bool on_stack = false;
409d89ec533Spatrick // Check stack first. If the address is on the stack of a live thread, we
410d89ec533Spatrick // know it cannot be a heap / global overflow.
411d89ec533Spatrick hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
412d89ec533Spatrick if (t->AddrIsInStack(untagged_addr)) {
413d89ec533Spatrick on_stack = true;
414d89ec533Spatrick // TODO(fmayer): figure out how to distinguish use-after-return and
415d89ec533Spatrick // stack-buffer-overflow.
416d89ec533Spatrick Printf("%s", d.Error());
417d89ec533Spatrick Printf("\nCause: stack tag-mismatch\n");
418d89ec533Spatrick Printf("%s", d.Location());
419d89ec533Spatrick Printf("Address %p is located in stack of thread T%zd\n", untagged_addr,
420d89ec533Spatrick t->unique_id());
421d89ec533Spatrick Printf("%s", d.Default());
422d89ec533Spatrick t->Announce();
423d89ec533Spatrick
424d89ec533Spatrick auto *sa = (t == GetCurrentThread() && current_stack_allocations)
425d89ec533Spatrick ? current_stack_allocations
426d89ec533Spatrick : t->stack_allocations();
427d89ec533Spatrick PrintStackAllocations(sa, addr_tag, untagged_addr);
428d89ec533Spatrick num_descriptions_printed++;
429d89ec533Spatrick }
430d89ec533Spatrick });
431d89ec533Spatrick
4323cab2bb3Spatrick // Check if this looks like a heap buffer overflow by scanning
4333cab2bb3Spatrick // the shadow left and right and looking for the first adjacent
4343cab2bb3Spatrick // object with a different memory tag. If that tag matches addr_tag,
4353cab2bb3Spatrick // check the allocator if it has a live chunk there.
4363cab2bb3Spatrick tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
4373cab2bb3Spatrick tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr;
438d89ec533Spatrick uptr candidate_distance = 0;
439d89ec533Spatrick for (; candidate_distance < 1000; candidate_distance++) {
440d89ec533Spatrick if (MemIsShadow(reinterpret_cast<uptr>(left)) &&
441d89ec533Spatrick TagsEqual(addr_tag, left)) {
4423cab2bb3Spatrick candidate = left;
4433cab2bb3Spatrick break;
4443cab2bb3Spatrick }
4453cab2bb3Spatrick --left;
446d89ec533Spatrick if (MemIsShadow(reinterpret_cast<uptr>(right)) &&
447d89ec533Spatrick TagsEqual(addr_tag, right)) {
4483cab2bb3Spatrick candidate = right;
4493cab2bb3Spatrick break;
4503cab2bb3Spatrick }
4513cab2bb3Spatrick ++right;
4523cab2bb3Spatrick }
4533cab2bb3Spatrick
454d89ec533Spatrick constexpr auto kCloseCandidateDistance = 1;
455d89ec533Spatrick
456d89ec533Spatrick if (!on_stack && candidate && candidate_distance <= kCloseCandidateDistance) {
457d89ec533Spatrick ShowHeapOrGlobalCandidate(untagged_addr, candidate, left, right);
4583cab2bb3Spatrick num_descriptions_printed++;
4593cab2bb3Spatrick }
4603cab2bb3Spatrick
4613cab2bb3Spatrick hwasanThreadList().VisitAllLiveThreads([&](Thread *t) {
4623cab2bb3Spatrick // Scan all threads' ring buffers to find if it's a heap-use-after-free.
4633cab2bb3Spatrick HeapAllocationRecord har;
4641f9cb04fSpatrick uptr ring_index, num_matching_addrs, num_matching_addrs_4b;
4651f9cb04fSpatrick if (FindHeapAllocation(t->heap_allocations(), tagged_addr, &har,
4661f9cb04fSpatrick &ring_index, &num_matching_addrs,
4671f9cb04fSpatrick &num_matching_addrs_4b)) {
468d89ec533Spatrick Printf("%s", d.Error());
469d89ec533Spatrick Printf("\nCause: use-after-free\n");
4703cab2bb3Spatrick Printf("%s", d.Location());
471*810390e3Srobert Printf("%p is located %zd bytes inside a %zd-byte region [%p,%p)\n",
4723cab2bb3Spatrick untagged_addr, untagged_addr - UntagAddr(har.tagged_addr),
4733cab2bb3Spatrick har.requested_size, UntagAddr(har.tagged_addr),
4743cab2bb3Spatrick UntagAddr(har.tagged_addr) + har.requested_size);
4753cab2bb3Spatrick Printf("%s", d.Allocation());
4763cab2bb3Spatrick Printf("freed by thread T%zd here:\n", t->unique_id());
4773cab2bb3Spatrick Printf("%s", d.Default());
4783cab2bb3Spatrick GetStackTraceFromId(har.free_context_id).Print();
4793cab2bb3Spatrick
4803cab2bb3Spatrick Printf("%s", d.Allocation());
4813cab2bb3Spatrick Printf("previously allocated here:\n", t);
4823cab2bb3Spatrick Printf("%s", d.Default());
4833cab2bb3Spatrick GetStackTraceFromId(har.alloc_context_id).Print();
4843cab2bb3Spatrick
4853cab2bb3Spatrick // Print a developer note: the index of this heap object
4863cab2bb3Spatrick // in the thread's deallocation ring buffer.
4871f9cb04fSpatrick Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", ring_index + 1,
4883cab2bb3Spatrick flags()->heap_history_size);
4891f9cb04fSpatrick Printf("hwasan_dev_note_num_matching_addrs: %zd\n", num_matching_addrs);
4901f9cb04fSpatrick Printf("hwasan_dev_note_num_matching_addrs_4b: %zd\n",
4911f9cb04fSpatrick num_matching_addrs_4b);
4923cab2bb3Spatrick
4933cab2bb3Spatrick t->Announce();
4943cab2bb3Spatrick num_descriptions_printed++;
4953cab2bb3Spatrick }
496d89ec533Spatrick });
4973cab2bb3Spatrick
498d89ec533Spatrick if (candidate && num_descriptions_printed == 0) {
499d89ec533Spatrick ShowHeapOrGlobalCandidate(untagged_addr, candidate, left, right);
5003cab2bb3Spatrick num_descriptions_printed++;
5013cab2bb3Spatrick }
5023cab2bb3Spatrick
5033cab2bb3Spatrick // Print the remaining threads, as an extra information, 1 line per thread.
5043cab2bb3Spatrick hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); });
5053cab2bb3Spatrick
5063cab2bb3Spatrick if (!num_descriptions_printed)
5073cab2bb3Spatrick // We exhausted our possibilities. Bail out.
5083cab2bb3Spatrick Printf("HWAddressSanitizer can not describe address in more detail.\n");
509d89ec533Spatrick if (num_descriptions_printed > 1) {
510d89ec533Spatrick Printf(
511d89ec533Spatrick "There are %d potential causes, printed above in order "
512d89ec533Spatrick "of likeliness.\n",
513d89ec533Spatrick num_descriptions_printed);
514d89ec533Spatrick }
5153cab2bb3Spatrick }
5163cab2bb3Spatrick
ReportStats()5173cab2bb3Spatrick void ReportStats() {}
5183cab2bb3Spatrick
PrintTagInfoAroundAddr(tag_t * tag_ptr,uptr num_rows,void (* print_tag)(InternalScopedString & s,tag_t * tag))5193cab2bb3Spatrick static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows,
5203cab2bb3Spatrick void (*print_tag)(InternalScopedString &s,
5213cab2bb3Spatrick tag_t *tag)) {
5223cab2bb3Spatrick const uptr row_len = 16; // better be power of two.
5233cab2bb3Spatrick tag_t *center_row_beg = reinterpret_cast<tag_t *>(
5243cab2bb3Spatrick RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len));
5253cab2bb3Spatrick tag_t *beg_row = center_row_beg - row_len * (num_rows / 2);
5263cab2bb3Spatrick tag_t *end_row = center_row_beg + row_len * ((num_rows + 1) / 2);
527d89ec533Spatrick InternalScopedString s;
5283cab2bb3Spatrick for (tag_t *row = beg_row; row < end_row; row += row_len) {
5293cab2bb3Spatrick s.append("%s", row == center_row_beg ? "=>" : " ");
530*810390e3Srobert s.append("%p:", (void *)ShadowToMem(reinterpret_cast<uptr>(row)));
5313cab2bb3Spatrick for (uptr i = 0; i < row_len; i++) {
5323cab2bb3Spatrick s.append("%s", row + i == tag_ptr ? "[" : " ");
5333cab2bb3Spatrick print_tag(s, &row[i]);
5343cab2bb3Spatrick s.append("%s", row + i == tag_ptr ? "]" : " ");
5353cab2bb3Spatrick }
5363cab2bb3Spatrick s.append("\n");
5373cab2bb3Spatrick }
5383cab2bb3Spatrick Printf("%s", s.data());
5393cab2bb3Spatrick }
5403cab2bb3Spatrick
PrintTagsAroundAddr(tag_t * tag_ptr)5413cab2bb3Spatrick static void PrintTagsAroundAddr(tag_t *tag_ptr) {
5423cab2bb3Spatrick Printf(
5433cab2bb3Spatrick "Memory tags around the buggy address (one tag corresponds to %zd "
5443cab2bb3Spatrick "bytes):\n", kShadowAlignment);
5453cab2bb3Spatrick PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) {
5463cab2bb3Spatrick s.append("%02x", *tag);
5473cab2bb3Spatrick });
5483cab2bb3Spatrick
5493cab2bb3Spatrick Printf(
5503cab2bb3Spatrick "Tags for short granules around the buggy address (one tag corresponds "
5513cab2bb3Spatrick "to %zd bytes):\n",
5523cab2bb3Spatrick kShadowAlignment);
5533cab2bb3Spatrick PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) {
5543cab2bb3Spatrick if (*tag >= 1 && *tag <= kShadowAlignment) {
5553cab2bb3Spatrick uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag));
5563cab2bb3Spatrick s.append("%02x",
5573cab2bb3Spatrick *reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1));
5583cab2bb3Spatrick } else {
5593cab2bb3Spatrick s.append("..");
5603cab2bb3Spatrick }
5613cab2bb3Spatrick });
5623cab2bb3Spatrick Printf(
5633cab2bb3Spatrick "See "
5643cab2bb3Spatrick "https://clang.llvm.org/docs/"
5653cab2bb3Spatrick "HardwareAssistedAddressSanitizerDesign.html#short-granules for a "
5663cab2bb3Spatrick "description of short granule tags\n");
5673cab2bb3Spatrick }
5683cab2bb3Spatrick
GetTopPc(StackTrace * stack)569*810390e3Srobert uptr GetTopPc(StackTrace *stack) {
570*810390e3Srobert return stack->size ? StackTrace::GetPreviousInstructionPc(stack->trace[0])
571*810390e3Srobert : 0;
572*810390e3Srobert }
573*810390e3Srobert
ReportInvalidFree(StackTrace * stack,uptr tagged_addr)5743cab2bb3Spatrick void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) {
5753cab2bb3Spatrick ScopedReport R(flags()->halt_on_error);
5763cab2bb3Spatrick
5773cab2bb3Spatrick uptr untagged_addr = UntagAddr(tagged_addr);
5783cab2bb3Spatrick tag_t ptr_tag = GetTagFromPointer(tagged_addr);
579*810390e3Srobert tag_t *tag_ptr = nullptr;
580*810390e3Srobert tag_t mem_tag = 0;
581*810390e3Srobert if (MemIsApp(untagged_addr)) {
582*810390e3Srobert tag_ptr = reinterpret_cast<tag_t *>(MemToShadow(untagged_addr));
583*810390e3Srobert if (MemIsShadow(reinterpret_cast<uptr>(tag_ptr)))
584*810390e3Srobert mem_tag = *tag_ptr;
585*810390e3Srobert else
586*810390e3Srobert tag_ptr = nullptr;
587*810390e3Srobert }
5883cab2bb3Spatrick Decorator d;
5893cab2bb3Spatrick Printf("%s", d.Error());
590*810390e3Srobert uptr pc = GetTopPc(stack);
5913cab2bb3Spatrick const char *bug_type = "invalid-free";
592*810390e3Srobert const Thread *thread = GetCurrentThread();
593*810390e3Srobert if (thread) {
594*810390e3Srobert Report("ERROR: %s: %s on address %p at pc %p on thread T%zd\n",
595*810390e3Srobert SanitizerToolName, bug_type, untagged_addr, pc, thread->unique_id());
596*810390e3Srobert } else {
597*810390e3Srobert Report("ERROR: %s: %s on address %p at pc %p on unknown thread\n",
598*810390e3Srobert SanitizerToolName, bug_type, untagged_addr, pc);
599*810390e3Srobert }
6003cab2bb3Spatrick Printf("%s", d.Access());
601*810390e3Srobert if (tag_ptr)
6023cab2bb3Spatrick Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag);
6033cab2bb3Spatrick Printf("%s", d.Default());
6043cab2bb3Spatrick
6053cab2bb3Spatrick stack->Print();
6063cab2bb3Spatrick
6073cab2bb3Spatrick PrintAddressDescription(tagged_addr, 0, nullptr);
6083cab2bb3Spatrick
609*810390e3Srobert if (tag_ptr)
6103cab2bb3Spatrick PrintTagsAroundAddr(tag_ptr);
6113cab2bb3Spatrick
612*810390e3Srobert MaybePrintAndroidHelpUrl();
6133cab2bb3Spatrick ReportErrorSummary(bug_type, stack);
6143cab2bb3Spatrick }
6153cab2bb3Spatrick
ReportTailOverwritten(StackTrace * stack,uptr tagged_addr,uptr orig_size,const u8 * expected)6163cab2bb3Spatrick void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size,
6173cab2bb3Spatrick const u8 *expected) {
6183cab2bb3Spatrick uptr tail_size = kShadowAlignment - (orig_size % kShadowAlignment);
619*810390e3Srobert u8 actual_expected[kShadowAlignment];
620*810390e3Srobert internal_memcpy(actual_expected, expected, tail_size);
621*810390e3Srobert tag_t ptr_tag = GetTagFromPointer(tagged_addr);
622*810390e3Srobert // Short granule is stashed in the last byte of the magic string. To avoid
623*810390e3Srobert // confusion, make the expected magic string contain the short granule tag.
624*810390e3Srobert if (orig_size % kShadowAlignment != 0) {
625*810390e3Srobert actual_expected[tail_size - 1] = ptr_tag;
626*810390e3Srobert }
627*810390e3Srobert
6283cab2bb3Spatrick ScopedReport R(flags()->halt_on_error);
6293cab2bb3Spatrick Decorator d;
6303cab2bb3Spatrick uptr untagged_addr = UntagAddr(tagged_addr);
6313cab2bb3Spatrick Printf("%s", d.Error());
6323cab2bb3Spatrick const char *bug_type = "allocation-tail-overwritten";
6333cab2bb3Spatrick Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName,
6343cab2bb3Spatrick bug_type, untagged_addr, untagged_addr + orig_size, orig_size);
6353cab2bb3Spatrick Printf("\n%s", d.Default());
636d89ec533Spatrick Printf(
637d89ec533Spatrick "Stack of invalid access unknown. Issue detected at deallocation "
638d89ec533Spatrick "time.\n");
639d89ec533Spatrick Printf("%s", d.Allocation());
640d89ec533Spatrick Printf("deallocated here:\n");
641d89ec533Spatrick Printf("%s", d.Default());
6423cab2bb3Spatrick stack->Print();
6433cab2bb3Spatrick HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr);
6443cab2bb3Spatrick if (chunk.Beg()) {
6453cab2bb3Spatrick Printf("%s", d.Allocation());
6463cab2bb3Spatrick Printf("allocated here:\n");
6473cab2bb3Spatrick Printf("%s", d.Default());
6483cab2bb3Spatrick GetStackTraceFromId(chunk.GetAllocStackId()).Print();
6493cab2bb3Spatrick }
6503cab2bb3Spatrick
651d89ec533Spatrick InternalScopedString s;
6523cab2bb3Spatrick CHECK_GT(tail_size, 0U);
6533cab2bb3Spatrick CHECK_LT(tail_size, kShadowAlignment);
6543cab2bb3Spatrick u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size);
6553cab2bb3Spatrick s.append("Tail contains: ");
6563cab2bb3Spatrick for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
6573cab2bb3Spatrick s.append(".. ");
6583cab2bb3Spatrick for (uptr i = 0; i < tail_size; i++)
6593cab2bb3Spatrick s.append("%02x ", tail[i]);
6603cab2bb3Spatrick s.append("\n");
6613cab2bb3Spatrick s.append("Expected: ");
6623cab2bb3Spatrick for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
6633cab2bb3Spatrick s.append(".. ");
664*810390e3Srobert for (uptr i = 0; i < tail_size; i++) s.append("%02x ", actual_expected[i]);
6653cab2bb3Spatrick s.append("\n");
6663cab2bb3Spatrick s.append(" ");
6673cab2bb3Spatrick for (uptr i = 0; i < kShadowAlignment - tail_size; i++)
6683cab2bb3Spatrick s.append(" ");
6693cab2bb3Spatrick for (uptr i = 0; i < tail_size; i++)
670*810390e3Srobert s.append("%s ", actual_expected[i] != tail[i] ? "^^" : " ");
6713cab2bb3Spatrick
6723cab2bb3Spatrick s.append("\nThis error occurs when a buffer overflow overwrites memory\n"
673*810390e3Srobert "after a heap object, but within the %zd-byte granule, e.g.\n"
6743cab2bb3Spatrick " char *x = new char[20];\n"
6753cab2bb3Spatrick " x[25] = 42;\n"
6763cab2bb3Spatrick "%s does not detect such bugs in uninstrumented code at the time of write,"
6773cab2bb3Spatrick "\nbut can detect them at the time of free/delete.\n"
6783cab2bb3Spatrick "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n",
6793cab2bb3Spatrick kShadowAlignment, SanitizerToolName);
6803cab2bb3Spatrick Printf("%s", s.data());
6813cab2bb3Spatrick GetCurrentThread()->Announce();
6823cab2bb3Spatrick
6833cab2bb3Spatrick tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr));
6843cab2bb3Spatrick PrintTagsAroundAddr(tag_ptr);
6853cab2bb3Spatrick
686*810390e3Srobert MaybePrintAndroidHelpUrl();
6873cab2bb3Spatrick ReportErrorSummary(bug_type, stack);
6883cab2bb3Spatrick }
6893cab2bb3Spatrick
ReportTagMismatch(StackTrace * stack,uptr tagged_addr,uptr access_size,bool is_store,bool fatal,uptr * registers_frame)6903cab2bb3Spatrick void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size,
6913cab2bb3Spatrick bool is_store, bool fatal, uptr *registers_frame) {
6923cab2bb3Spatrick ScopedReport R(fatal);
6933cab2bb3Spatrick SavedStackAllocations current_stack_allocations(
6943cab2bb3Spatrick GetCurrentThread()->stack_allocations());
6953cab2bb3Spatrick
6963cab2bb3Spatrick Decorator d;
6973cab2bb3Spatrick uptr untagged_addr = UntagAddr(tagged_addr);
6983cab2bb3Spatrick // TODO: when possible, try to print heap-use-after-free, etc.
6993cab2bb3Spatrick const char *bug_type = "tag-mismatch";
700*810390e3Srobert uptr pc = GetTopPc(stack);
701*810390e3Srobert Printf("%s", d.Error());
7023cab2bb3Spatrick Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type,
7033cab2bb3Spatrick untagged_addr, pc);
7043cab2bb3Spatrick
7053cab2bb3Spatrick Thread *t = GetCurrentThread();
7063cab2bb3Spatrick
7073cab2bb3Spatrick sptr offset =
7083cab2bb3Spatrick __hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size);
7093cab2bb3Spatrick CHECK(offset >= 0 && offset < static_cast<sptr>(access_size));
7103cab2bb3Spatrick tag_t ptr_tag = GetTagFromPointer(tagged_addr);
7113cab2bb3Spatrick tag_t *tag_ptr =
7123cab2bb3Spatrick reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset));
7133cab2bb3Spatrick tag_t mem_tag = *tag_ptr;
7143cab2bb3Spatrick
7153cab2bb3Spatrick Printf("%s", d.Access());
716*810390e3Srobert if (mem_tag && mem_tag < kShadowAlignment) {
717*810390e3Srobert tag_t *granule_ptr = reinterpret_cast<tag_t *>((untagged_addr + offset) &
718*810390e3Srobert ~(kShadowAlignment - 1));
719*810390e3Srobert // If offset is 0, (untagged_addr + offset) is not aligned to granules.
720*810390e3Srobert // This is the offset of the leftmost accessed byte within the bad granule.
721*810390e3Srobert u8 in_granule_offset = (untagged_addr + offset) & (kShadowAlignment - 1);
722*810390e3Srobert tag_t short_tag = granule_ptr[kShadowAlignment - 1];
723*810390e3Srobert // The first mismatch was a short granule that matched the ptr_tag.
724*810390e3Srobert if (short_tag == ptr_tag) {
725*810390e3Srobert // If the access starts after the end of the short granule, then the first
726*810390e3Srobert // bad byte is the first byte of the access; otherwise it is the first
727*810390e3Srobert // byte past the end of the short granule
728*810390e3Srobert if (mem_tag > in_granule_offset) {
729*810390e3Srobert offset += mem_tag - in_granule_offset;
730*810390e3Srobert }
731*810390e3Srobert }
732*810390e3Srobert Printf(
733*810390e3Srobert "%s of size %zu at %p tags: %02x/%02x(%02x) (ptr/mem) in thread T%zd\n",
734*810390e3Srobert is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
735*810390e3Srobert mem_tag, short_tag, t->unique_id());
736*810390e3Srobert } else {
7373cab2bb3Spatrick Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n",
7383cab2bb3Spatrick is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag,
7393cab2bb3Spatrick mem_tag, t->unique_id());
740*810390e3Srobert }
7413cab2bb3Spatrick if (offset != 0)
742*810390e3Srobert Printf("Invalid access starting at offset %zu\n", offset);
7433cab2bb3Spatrick Printf("%s", d.Default());
7443cab2bb3Spatrick
7453cab2bb3Spatrick stack->Print();
7463cab2bb3Spatrick
7473cab2bb3Spatrick PrintAddressDescription(tagged_addr, access_size,
7483cab2bb3Spatrick current_stack_allocations.get());
7493cab2bb3Spatrick t->Announce();
7503cab2bb3Spatrick
7513cab2bb3Spatrick PrintTagsAroundAddr(tag_ptr);
7523cab2bb3Spatrick
7533cab2bb3Spatrick if (registers_frame)
7543cab2bb3Spatrick ReportRegisters(registers_frame, pc);
7553cab2bb3Spatrick
756*810390e3Srobert MaybePrintAndroidHelpUrl();
7573cab2bb3Spatrick ReportErrorSummary(bug_type, stack);
7583cab2bb3Spatrick }
7593cab2bb3Spatrick
7603cab2bb3Spatrick // See the frame breakdown defined in __hwasan_tag_mismatch (from
761*810390e3Srobert // hwasan_tag_mismatch_{aarch64,riscv64}.S).
ReportRegisters(uptr * frame,uptr pc)7623cab2bb3Spatrick void ReportRegisters(uptr *frame, uptr pc) {
7633cab2bb3Spatrick Printf("Registers where the failure occurred (pc %p):\n", pc);
7643cab2bb3Spatrick
7653cab2bb3Spatrick // We explicitly print a single line (4 registers/line) each iteration to
7663cab2bb3Spatrick // reduce the amount of logcat error messages printed. Each Printf() will
7673cab2bb3Spatrick // result in a new logcat line, irrespective of whether a newline is present,
7683cab2bb3Spatrick // and so we wish to reduce the number of Printf() calls we have to make.
769*810390e3Srobert #if defined(__aarch64__)
7703cab2bb3Spatrick Printf(" x0 %016llx x1 %016llx x2 %016llx x3 %016llx\n",
7713cab2bb3Spatrick frame[0], frame[1], frame[2], frame[3]);
772*810390e3Srobert #elif SANITIZER_RISCV64
773*810390e3Srobert Printf(" sp %016llx x1 %016llx x2 %016llx x3 %016llx\n",
774*810390e3Srobert reinterpret_cast<u8 *>(frame) + 256, frame[1], frame[2], frame[3]);
775*810390e3Srobert #endif
7763cab2bb3Spatrick Printf(" x4 %016llx x5 %016llx x6 %016llx x7 %016llx\n",
7773cab2bb3Spatrick frame[4], frame[5], frame[6], frame[7]);
7783cab2bb3Spatrick Printf(" x8 %016llx x9 %016llx x10 %016llx x11 %016llx\n",
7793cab2bb3Spatrick frame[8], frame[9], frame[10], frame[11]);
7803cab2bb3Spatrick Printf(" x12 %016llx x13 %016llx x14 %016llx x15 %016llx\n",
7813cab2bb3Spatrick frame[12], frame[13], frame[14], frame[15]);
7823cab2bb3Spatrick Printf(" x16 %016llx x17 %016llx x18 %016llx x19 %016llx\n",
7833cab2bb3Spatrick frame[16], frame[17], frame[18], frame[19]);
7843cab2bb3Spatrick Printf(" x20 %016llx x21 %016llx x22 %016llx x23 %016llx\n",
7853cab2bb3Spatrick frame[20], frame[21], frame[22], frame[23]);
7863cab2bb3Spatrick Printf(" x24 %016llx x25 %016llx x26 %016llx x27 %016llx\n",
7873cab2bb3Spatrick frame[24], frame[25], frame[26], frame[27]);
788d89ec533Spatrick // hwasan_check* reduces the stack pointer by 256, then __hwasan_tag_mismatch
789d89ec533Spatrick // passes it to this function.
790*810390e3Srobert #if defined(__aarch64__)
791d89ec533Spatrick Printf(" x28 %016llx x29 %016llx x30 %016llx sp %016llx\n", frame[28],
792d89ec533Spatrick frame[29], frame[30], reinterpret_cast<u8 *>(frame) + 256);
793*810390e3Srobert #elif SANITIZER_RISCV64
794*810390e3Srobert Printf(" x28 %016llx x29 %016llx x30 %016llx x31 %016llx\n", frame[28],
795*810390e3Srobert frame[29], frame[30], frame[31]);
796*810390e3Srobert #else
797*810390e3Srobert #endif
7983cab2bb3Spatrick }
7993cab2bb3Spatrick
8003cab2bb3Spatrick } // namespace __hwasan
801d89ec533Spatrick
__hwasan_set_error_report_callback(void (* callback)(const char *))802d89ec533Spatrick void __hwasan_set_error_report_callback(void (*callback)(const char *)) {
803d89ec533Spatrick __hwasan::ScopedReport::SetErrorReportCallback(callback);
804d89ec533Spatrick }
805