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.h" 15 #include "hwasan_allocator.h" 16 #include "hwasan_mapping.h" 17 #include "hwasan_report.h" 18 #include "hwasan_thread.h" 19 #include "hwasan_thread_list.h" 20 #include "sanitizer_common/sanitizer_allocator_internal.h" 21 #include "sanitizer_common/sanitizer_common.h" 22 #include "sanitizer_common/sanitizer_flags.h" 23 #include "sanitizer_common/sanitizer_mutex.h" 24 #include "sanitizer_common/sanitizer_report_decorator.h" 25 #include "sanitizer_common/sanitizer_stackdepot.h" 26 #include "sanitizer_common/sanitizer_stacktrace_printer.h" 27 #include "sanitizer_common/sanitizer_symbolizer.h" 28 29 using namespace __sanitizer; 30 31 namespace __hwasan { 32 33 class ScopedReport { 34 public: 35 ScopedReport(bool fatal = false) : error_message_(1), fatal(fatal) { 36 BlockingMutexLock lock(&error_message_lock_); 37 error_message_ptr_ = fatal ? &error_message_ : nullptr; 38 ++hwasan_report_count; 39 } 40 41 ~ScopedReport() { 42 { 43 BlockingMutexLock lock(&error_message_lock_); 44 if (fatal) 45 SetAbortMessage(error_message_.data()); 46 error_message_ptr_ = nullptr; 47 } 48 if (common_flags()->print_module_map >= 2 || 49 (fatal && common_flags()->print_module_map)) 50 DumpProcessMap(); 51 if (fatal) 52 Die(); 53 } 54 55 static void MaybeAppendToErrorMessage(const char *msg) { 56 BlockingMutexLock lock(&error_message_lock_); 57 if (!error_message_ptr_) 58 return; 59 uptr len = internal_strlen(msg); 60 uptr old_size = error_message_ptr_->size(); 61 error_message_ptr_->resize(old_size + len); 62 // overwrite old trailing '\0', keep new trailing '\0' untouched. 63 internal_memcpy(&(*error_message_ptr_)[old_size - 1], msg, len); 64 } 65 private: 66 ScopedErrorReportLock error_report_lock_; 67 InternalMmapVector<char> error_message_; 68 bool fatal; 69 70 static InternalMmapVector<char> *error_message_ptr_; 71 static BlockingMutex error_message_lock_; 72 }; 73 74 InternalMmapVector<char> *ScopedReport::error_message_ptr_; 75 BlockingMutex ScopedReport::error_message_lock_; 76 77 // If there is an active ScopedReport, append to its error message. 78 void AppendToErrorMessageBuffer(const char *buffer) { 79 ScopedReport::MaybeAppendToErrorMessage(buffer); 80 } 81 82 static StackTrace GetStackTraceFromId(u32 id) { 83 CHECK(id); 84 StackTrace res = StackDepotGet(id); 85 CHECK(res.trace); 86 return res; 87 } 88 89 // A RAII object that holds a copy of the current thread stack ring buffer. 90 // The actual stack buffer may change while we are iterating over it (for 91 // example, Printf may call syslog() which can itself be built with hwasan). 92 class SavedStackAllocations { 93 public: 94 SavedStackAllocations(StackAllocationsRingBuffer *rb) { 95 uptr size = rb->size() * sizeof(uptr); 96 void *storage = 97 MmapAlignedOrDieOnFatalError(size, size * 2, "saved stack allocations"); 98 new (&rb_) StackAllocationsRingBuffer(*rb, storage); 99 } 100 101 ~SavedStackAllocations() { 102 StackAllocationsRingBuffer *rb = get(); 103 UnmapOrDie(rb->StartOfStorage(), rb->size() * sizeof(uptr)); 104 } 105 106 StackAllocationsRingBuffer *get() { 107 return (StackAllocationsRingBuffer *)&rb_; 108 } 109 110 private: 111 uptr rb_; 112 }; 113 114 class Decorator: public __sanitizer::SanitizerCommonDecorator { 115 public: 116 Decorator() : SanitizerCommonDecorator() { } 117 const char *Access() { return Blue(); } 118 const char *Allocation() const { return Magenta(); } 119 const char *Origin() const { return Magenta(); } 120 const char *Name() const { return Green(); } 121 const char *Location() { return Green(); } 122 const char *Thread() { return Green(); } 123 }; 124 125 // Returns the index of the rb element that matches tagged_addr (plus one), 126 // or zero if found nothing. 127 uptr FindHeapAllocation(HeapAllocationsRingBuffer *rb, 128 uptr tagged_addr, 129 HeapAllocationRecord *har) { 130 if (!rb) return 0; 131 for (uptr i = 0, size = rb->size(); i < size; i++) { 132 auto h = (*rb)[i]; 133 if (h.tagged_addr <= tagged_addr && 134 h.tagged_addr + h.requested_size > tagged_addr) { 135 *har = h; 136 return i + 1; 137 } 138 } 139 return 0; 140 } 141 142 static void PrintStackAllocations(StackAllocationsRingBuffer *sa, 143 tag_t addr_tag, uptr untagged_addr) { 144 uptr frames = Min((uptr)flags()->stack_history_size, sa->size()); 145 bool found_local = false; 146 for (uptr i = 0; i < frames; i++) { 147 const uptr *record_addr = &(*sa)[i]; 148 uptr record = *record_addr; 149 if (!record) 150 break; 151 tag_t base_tag = 152 reinterpret_cast<uptr>(record_addr) >> kRecordAddrBaseTagShift; 153 uptr fp = (record >> kRecordFPShift) << kRecordFPLShift; 154 uptr pc_mask = (1ULL << kRecordFPShift) - 1; 155 uptr pc = record & pc_mask; 156 FrameInfo frame; 157 if (Symbolizer::GetOrInit()->SymbolizeFrame(pc, &frame)) { 158 for (LocalInfo &local : frame.locals) { 159 if (!local.has_frame_offset || !local.has_size || !local.has_tag_offset) 160 continue; 161 tag_t obj_tag = base_tag ^ local.tag_offset; 162 if (obj_tag != addr_tag) 163 continue; 164 // Calculate the offset from the object address to the faulting 165 // address. Because we only store bits 4-19 of FP (bits 0-3 are 166 // guaranteed to be zero), the calculation is performed mod 2^20 and may 167 // harmlessly underflow if the address mod 2^20 is below the object 168 // address. 169 uptr obj_offset = 170 (untagged_addr - fp - local.frame_offset) & (kRecordFPModulus - 1); 171 if (obj_offset >= local.size) 172 continue; 173 if (!found_local) { 174 Printf("Potentially referenced stack objects:\n"); 175 found_local = true; 176 } 177 Printf(" %s in %s %s:%d\n", local.name, local.function_name, 178 local.decl_file, local.decl_line); 179 } 180 frame.Clear(); 181 } 182 } 183 184 if (found_local) 185 return; 186 187 // We didn't find any locals. Most likely we don't have symbols, so dump 188 // the information that we have for offline analysis. 189 InternalScopedString frame_desc(GetPageSizeCached() * 2); 190 Printf("Previously allocated frames:\n"); 191 for (uptr i = 0; i < frames; i++) { 192 const uptr *record_addr = &(*sa)[i]; 193 uptr record = *record_addr; 194 if (!record) 195 break; 196 uptr pc_mask = (1ULL << 48) - 1; 197 uptr pc = record & pc_mask; 198 frame_desc.append(" record_addr:0x%zx record:0x%zx", 199 reinterpret_cast<uptr>(record_addr), record); 200 if (SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc)) { 201 RenderFrame(&frame_desc, " %F %L\n", 0, frame->info, 202 common_flags()->symbolize_vs_style, 203 common_flags()->strip_path_prefix); 204 frame->ClearAll(); 205 } 206 Printf("%s", frame_desc.data()); 207 frame_desc.clear(); 208 } 209 } 210 211 // Returns true if tag == *tag_ptr, reading tags from short granules if 212 // necessary. This may return a false positive if tags 1-15 are used as a 213 // regular tag rather than a short granule marker. 214 static bool TagsEqual(tag_t tag, tag_t *tag_ptr) { 215 if (tag == *tag_ptr) 216 return true; 217 if (*tag_ptr == 0 || *tag_ptr > kShadowAlignment - 1) 218 return false; 219 uptr mem = ShadowToMem(reinterpret_cast<uptr>(tag_ptr)); 220 tag_t inline_tag = *reinterpret_cast<tag_t *>(mem + kShadowAlignment - 1); 221 return tag == inline_tag; 222 } 223 224 void PrintAddressDescription( 225 uptr tagged_addr, uptr access_size, 226 StackAllocationsRingBuffer *current_stack_allocations) { 227 Decorator d; 228 int num_descriptions_printed = 0; 229 uptr untagged_addr = UntagAddr(tagged_addr); 230 231 // Print some very basic information about the address, if it's a heap. 232 HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr); 233 if (uptr beg = chunk.Beg()) { 234 uptr size = chunk.ActualSize(); 235 Printf("%s[%p,%p) is a %s %s heap chunk; " 236 "size: %zd offset: %zd\n%s", 237 d.Location(), 238 beg, beg + size, 239 chunk.FromSmallHeap() ? "small" : "large", 240 chunk.IsAllocated() ? "allocated" : "unallocated", 241 size, untagged_addr - beg, 242 d.Default()); 243 } 244 245 // Check if this looks like a heap buffer overflow by scanning 246 // the shadow left and right and looking for the first adjacent 247 // object with a different memory tag. If that tag matches addr_tag, 248 // check the allocator if it has a live chunk there. 249 tag_t addr_tag = GetTagFromPointer(tagged_addr); 250 tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr)); 251 tag_t *candidate = nullptr, *left = tag_ptr, *right = tag_ptr; 252 for (int i = 0; i < 1000; i++) { 253 if (TagsEqual(addr_tag, left)) { 254 candidate = left; 255 break; 256 } 257 --left; 258 if (TagsEqual(addr_tag, right)) { 259 candidate = right; 260 break; 261 } 262 ++right; 263 } 264 265 if (candidate) { 266 uptr mem = ShadowToMem(reinterpret_cast<uptr>(candidate)); 267 HwasanChunkView chunk = FindHeapChunkByAddress(mem); 268 if (chunk.IsAllocated()) { 269 Printf("%s", d.Location()); 270 Printf("%p is located %zd bytes to the %s of %zd-byte region [%p,%p)\n", 271 untagged_addr, 272 candidate == left ? untagged_addr - chunk.End() 273 : chunk.Beg() - untagged_addr, 274 candidate == left ? "right" : "left", chunk.UsedSize(), 275 chunk.Beg(), chunk.End()); 276 Printf("%s", d.Allocation()); 277 Printf("allocated here:\n"); 278 Printf("%s", d.Default()); 279 GetStackTraceFromId(chunk.GetAllocStackId()).Print(); 280 num_descriptions_printed++; 281 } 282 } 283 284 hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { 285 // Scan all threads' ring buffers to find if it's a heap-use-after-free. 286 HeapAllocationRecord har; 287 if (uptr D = FindHeapAllocation(t->heap_allocations(), tagged_addr, &har)) { 288 Printf("%s", d.Location()); 289 Printf("%p is located %zd bytes inside of %zd-byte region [%p,%p)\n", 290 untagged_addr, untagged_addr - UntagAddr(har.tagged_addr), 291 har.requested_size, UntagAddr(har.tagged_addr), 292 UntagAddr(har.tagged_addr) + har.requested_size); 293 Printf("%s", d.Allocation()); 294 Printf("freed by thread T%zd here:\n", t->unique_id()); 295 Printf("%s", d.Default()); 296 GetStackTraceFromId(har.free_context_id).Print(); 297 298 Printf("%s", d.Allocation()); 299 Printf("previously allocated here:\n", t); 300 Printf("%s", d.Default()); 301 GetStackTraceFromId(har.alloc_context_id).Print(); 302 303 // Print a developer note: the index of this heap object 304 // in the thread's deallocation ring buffer. 305 Printf("hwasan_dev_note_heap_rb_distance: %zd %zd\n", D, 306 flags()->heap_history_size); 307 308 t->Announce(); 309 num_descriptions_printed++; 310 } 311 312 // Very basic check for stack memory. 313 if (t->AddrIsInStack(untagged_addr)) { 314 Printf("%s", d.Location()); 315 Printf("Address %p is located in stack of thread T%zd\n", untagged_addr, 316 t->unique_id()); 317 Printf("%s", d.Default()); 318 t->Announce(); 319 320 auto *sa = (t == GetCurrentThread() && current_stack_allocations) 321 ? current_stack_allocations 322 : t->stack_allocations(); 323 PrintStackAllocations(sa, addr_tag, untagged_addr); 324 num_descriptions_printed++; 325 } 326 }); 327 328 // Print the remaining threads, as an extra information, 1 line per thread. 329 hwasanThreadList().VisitAllLiveThreads([&](Thread *t) { t->Announce(); }); 330 331 if (!num_descriptions_printed) 332 // We exhausted our possibilities. Bail out. 333 Printf("HWAddressSanitizer can not describe address in more detail.\n"); 334 } 335 336 void ReportStats() {} 337 338 static void PrintTagInfoAroundAddr(tag_t *tag_ptr, uptr num_rows, 339 void (*print_tag)(InternalScopedString &s, 340 tag_t *tag)) { 341 const uptr row_len = 16; // better be power of two. 342 tag_t *center_row_beg = reinterpret_cast<tag_t *>( 343 RoundDownTo(reinterpret_cast<uptr>(tag_ptr), row_len)); 344 tag_t *beg_row = center_row_beg - row_len * (num_rows / 2); 345 tag_t *end_row = center_row_beg + row_len * ((num_rows + 1) / 2); 346 InternalScopedString s(GetPageSizeCached() * 8); 347 for (tag_t *row = beg_row; row < end_row; row += row_len) { 348 s.append("%s", row == center_row_beg ? "=>" : " "); 349 for (uptr i = 0; i < row_len; i++) { 350 s.append("%s", row + i == tag_ptr ? "[" : " "); 351 print_tag(s, &row[i]); 352 s.append("%s", row + i == tag_ptr ? "]" : " "); 353 } 354 s.append("%s\n", row == center_row_beg ? "<=" : " "); 355 } 356 Printf("%s", s.data()); 357 } 358 359 static void PrintTagsAroundAddr(tag_t *tag_ptr) { 360 Printf( 361 "Memory tags around the buggy address (one tag corresponds to %zd " 362 "bytes):\n", kShadowAlignment); 363 PrintTagInfoAroundAddr(tag_ptr, 17, [](InternalScopedString &s, tag_t *tag) { 364 s.append("%02x", *tag); 365 }); 366 367 Printf( 368 "Tags for short granules around the buggy address (one tag corresponds " 369 "to %zd bytes):\n", 370 kShadowAlignment); 371 PrintTagInfoAroundAddr(tag_ptr, 3, [](InternalScopedString &s, tag_t *tag) { 372 if (*tag >= 1 && *tag <= kShadowAlignment) { 373 uptr granule_addr = ShadowToMem(reinterpret_cast<uptr>(tag)); 374 s.append("%02x", 375 *reinterpret_cast<u8 *>(granule_addr + kShadowAlignment - 1)); 376 } else { 377 s.append(".."); 378 } 379 }); 380 Printf( 381 "See " 382 "https://clang.llvm.org/docs/" 383 "HardwareAssistedAddressSanitizerDesign.html#short-granules for a " 384 "description of short granule tags\n"); 385 } 386 387 void ReportInvalidFree(StackTrace *stack, uptr tagged_addr) { 388 ScopedReport R(flags()->halt_on_error); 389 390 uptr untagged_addr = UntagAddr(tagged_addr); 391 tag_t ptr_tag = GetTagFromPointer(tagged_addr); 392 tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr)); 393 tag_t mem_tag = *tag_ptr; 394 Decorator d; 395 Printf("%s", d.Error()); 396 uptr pc = stack->size ? stack->trace[0] : 0; 397 const char *bug_type = "invalid-free"; 398 Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type, 399 untagged_addr, pc); 400 Printf("%s", d.Access()); 401 Printf("tags: %02x/%02x (ptr/mem)\n", ptr_tag, mem_tag); 402 Printf("%s", d.Default()); 403 404 stack->Print(); 405 406 PrintAddressDescription(tagged_addr, 0, nullptr); 407 408 PrintTagsAroundAddr(tag_ptr); 409 410 ReportErrorSummary(bug_type, stack); 411 } 412 413 void ReportTailOverwritten(StackTrace *stack, uptr tagged_addr, uptr orig_size, 414 const u8 *expected) { 415 uptr tail_size = kShadowAlignment - (orig_size % kShadowAlignment); 416 ScopedReport R(flags()->halt_on_error); 417 Decorator d; 418 uptr untagged_addr = UntagAddr(tagged_addr); 419 Printf("%s", d.Error()); 420 const char *bug_type = "alocation-tail-overwritten"; 421 Report("ERROR: %s: %s; heap object [%p,%p) of size %zd\n", SanitizerToolName, 422 bug_type, untagged_addr, untagged_addr + orig_size, orig_size); 423 Printf("\n%s", d.Default()); 424 stack->Print(); 425 HwasanChunkView chunk = FindHeapChunkByAddress(untagged_addr); 426 if (chunk.Beg()) { 427 Printf("%s", d.Allocation()); 428 Printf("allocated here:\n"); 429 Printf("%s", d.Default()); 430 GetStackTraceFromId(chunk.GetAllocStackId()).Print(); 431 } 432 433 InternalScopedString s(GetPageSizeCached() * 8); 434 CHECK_GT(tail_size, 0U); 435 CHECK_LT(tail_size, kShadowAlignment); 436 u8 *tail = reinterpret_cast<u8*>(untagged_addr + orig_size); 437 s.append("Tail contains: "); 438 for (uptr i = 0; i < kShadowAlignment - tail_size; i++) 439 s.append(".. "); 440 for (uptr i = 0; i < tail_size; i++) 441 s.append("%02x ", tail[i]); 442 s.append("\n"); 443 s.append("Expected: "); 444 for (uptr i = 0; i < kShadowAlignment - tail_size; i++) 445 s.append(".. "); 446 for (uptr i = 0; i < tail_size; i++) 447 s.append("%02x ", expected[i]); 448 s.append("\n"); 449 s.append(" "); 450 for (uptr i = 0; i < kShadowAlignment - tail_size; i++) 451 s.append(" "); 452 for (uptr i = 0; i < tail_size; i++) 453 s.append("%s ", expected[i] != tail[i] ? "^^" : " "); 454 455 s.append("\nThis error occurs when a buffer overflow overwrites memory\n" 456 "to the right of a heap object, but within the %zd-byte granule, e.g.\n" 457 " char *x = new char[20];\n" 458 " x[25] = 42;\n" 459 "%s does not detect such bugs in uninstrumented code at the time of write," 460 "\nbut can detect them at the time of free/delete.\n" 461 "To disable this feature set HWASAN_OPTIONS=free_checks_tail_magic=0\n", 462 kShadowAlignment, SanitizerToolName); 463 Printf("%s", s.data()); 464 GetCurrentThread()->Announce(); 465 466 tag_t *tag_ptr = reinterpret_cast<tag_t*>(MemToShadow(untagged_addr)); 467 PrintTagsAroundAddr(tag_ptr); 468 469 ReportErrorSummary(bug_type, stack); 470 } 471 472 void ReportTagMismatch(StackTrace *stack, uptr tagged_addr, uptr access_size, 473 bool is_store, bool fatal, uptr *registers_frame) { 474 ScopedReport R(fatal); 475 SavedStackAllocations current_stack_allocations( 476 GetCurrentThread()->stack_allocations()); 477 478 Decorator d; 479 Printf("%s", d.Error()); 480 uptr untagged_addr = UntagAddr(tagged_addr); 481 // TODO: when possible, try to print heap-use-after-free, etc. 482 const char *bug_type = "tag-mismatch"; 483 uptr pc = stack->size ? stack->trace[0] : 0; 484 Report("ERROR: %s: %s on address %p at pc %p\n", SanitizerToolName, bug_type, 485 untagged_addr, pc); 486 487 Thread *t = GetCurrentThread(); 488 489 sptr offset = 490 __hwasan_test_shadow(reinterpret_cast<void *>(tagged_addr), access_size); 491 CHECK(offset >= 0 && offset < static_cast<sptr>(access_size)); 492 tag_t ptr_tag = GetTagFromPointer(tagged_addr); 493 tag_t *tag_ptr = 494 reinterpret_cast<tag_t *>(MemToShadow(untagged_addr + offset)); 495 tag_t mem_tag = *tag_ptr; 496 497 Printf("%s", d.Access()); 498 Printf("%s of size %zu at %p tags: %02x/%02x (ptr/mem) in thread T%zd\n", 499 is_store ? "WRITE" : "READ", access_size, untagged_addr, ptr_tag, 500 mem_tag, t->unique_id()); 501 if (offset != 0) 502 Printf("Invalid access starting at offset [%zu, %zu)\n", offset, 503 Min(access_size, static_cast<uptr>(offset) + (1 << kShadowScale))); 504 Printf("%s", d.Default()); 505 506 stack->Print(); 507 508 PrintAddressDescription(tagged_addr, access_size, 509 current_stack_allocations.get()); 510 t->Announce(); 511 512 PrintTagsAroundAddr(tag_ptr); 513 514 if (registers_frame) 515 ReportRegisters(registers_frame, pc); 516 517 ReportErrorSummary(bug_type, stack); 518 } 519 520 // See the frame breakdown defined in __hwasan_tag_mismatch (from 521 // hwasan_tag_mismatch_aarch64.S). 522 void ReportRegisters(uptr *frame, uptr pc) { 523 Printf("Registers where the failure occurred (pc %p):\n", pc); 524 525 // We explicitly print a single line (4 registers/line) each iteration to 526 // reduce the amount of logcat error messages printed. Each Printf() will 527 // result in a new logcat line, irrespective of whether a newline is present, 528 // and so we wish to reduce the number of Printf() calls we have to make. 529 Printf(" x0 %016llx x1 %016llx x2 %016llx x3 %016llx\n", 530 frame[0], frame[1], frame[2], frame[3]); 531 Printf(" x4 %016llx x5 %016llx x6 %016llx x7 %016llx\n", 532 frame[4], frame[5], frame[6], frame[7]); 533 Printf(" x8 %016llx x9 %016llx x10 %016llx x11 %016llx\n", 534 frame[8], frame[9], frame[10], frame[11]); 535 Printf(" x12 %016llx x13 %016llx x14 %016llx x15 %016llx\n", 536 frame[12], frame[13], frame[14], frame[15]); 537 Printf(" x16 %016llx x17 %016llx x18 %016llx x19 %016llx\n", 538 frame[16], frame[17], frame[18], frame[19]); 539 Printf(" x20 %016llx x21 %016llx x22 %016llx x23 %016llx\n", 540 frame[20], frame[21], frame[22], frame[23]); 541 Printf(" x24 %016llx x25 %016llx x26 %016llx x27 %016llx\n", 542 frame[24], frame[25], frame[26], frame[27]); 543 Printf(" x28 %016llx x29 %016llx x30 %016llx\n", 544 frame[28], frame[29], frame[30]); 545 } 546 547 } // namespace __hwasan 548