526 lines · cpp
1//===-- asan_descriptions.cpp -----------------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file is a part of AddressSanitizer, an address sanity checker.10//11// ASan functions for getting information about an address and/or printing it.12//===----------------------------------------------------------------------===//13 14#include "asan_descriptions.h"15#include "asan_mapping.h"16#include "asan_report.h"17#include "asan_stack.h"18#include "sanitizer_common/sanitizer_stackdepot.h"19 20namespace __asan {21 22AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {23 if (!t) {24 internal_snprintf(name, sizeof(name), "T-1");25 return;26 }27 int len = internal_snprintf(name, sizeof(name), "T%llu", t->unique_id);28 CHECK(((unsigned int)len) < sizeof(name));29 if (internal_strlen(t->name))30 internal_snprintf(&name[len], sizeof(name) - len, " (%s)", t->name);31}32 33AsanThreadIdAndName::AsanThreadIdAndName(u32 tid)34 : AsanThreadIdAndName(35 tid == kInvalidTid ? nullptr : GetThreadContextByTidLocked(tid)) {36 asanThreadRegistry().CheckLocked();37}38 39void DescribeThread(AsanThreadContext *context) {40 CHECK(context);41 asanThreadRegistry().CheckLocked();42 // No need to announce the main thread.43 if (context->tid == kMainTid || context->announced) {44 return;45 }46 context->announced = true;47 48 InternalScopedString str;49 str.AppendF("Thread %s", AsanThreadIdAndName(context).c_str());50 51 AsanThreadContext *parent_context =52 context->parent_tid == kInvalidTid53 ? nullptr54 : GetThreadContextByTidLocked(context->parent_tid);55 56 // `context->parent_tid` may point to reused slot. Check `unique_id` which57 // is always smaller for the parent, always greater for a new user.58 if (!parent_context || context->unique_id <= parent_context->unique_id) {59 str.Append(" created by unknown thread\n");60 Printf("%s", str.data());61 return;62 }63 str.AppendF(" created by %s here:\n",64 AsanThreadIdAndName(context->parent_tid).c_str());65 Printf("%s", str.data());66 StackDepotGet(context->stack_id).Print();67 // Recursively described parent thread if needed.68 if (flags()->print_full_thread_history)69 DescribeThread(parent_context);70}71 72// Shadow descriptions73static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {74 CHECK(!AddrIsInMem(addr));75 if (AddrIsInShadowGap(addr)) {76 *shadow_kind = kShadowKindGap;77 } else if (AddrIsInHighShadow(addr)) {78 *shadow_kind = kShadowKindHigh;79 } else if (AddrIsInLowShadow(addr)) {80 *shadow_kind = kShadowKindLow;81 } else {82 return false;83 }84 return true;85}86 87bool DescribeAddressIfShadow(uptr addr) {88 ShadowAddressDescription descr;89 if (!GetShadowAddressInformation(addr, &descr)) return false;90 descr.Print();91 return true;92}93 94bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {95 if (AddrIsInMem(addr)) return false;96 ShadowKind shadow_kind;97 if (!GetShadowKind(addr, &shadow_kind)) return false;98 if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;99 descr->addr = addr;100 descr->kind = shadow_kind;101 return true;102}103 104// Heap descriptions105static void GetAccessToHeapChunkInformation(ChunkAccess *descr,106 AsanChunkView chunk, uptr addr,107 uptr access_size) {108 descr->bad_addr = addr;109 if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {110 descr->access_type = kAccessTypeLeft;111 } else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {112 descr->access_type = kAccessTypeRight;113 if (descr->offset < 0) {114 descr->bad_addr -= descr->offset;115 descr->offset = 0;116 }117 } else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {118 descr->access_type = kAccessTypeInside;119 } else {120 descr->access_type = kAccessTypeUnknown;121 }122 descr->chunk_begin = chunk.Beg();123 descr->chunk_size = chunk.UsedSize();124 descr->user_requested_alignment = chunk.UserRequestedAlignment();125 descr->alloc_type = chunk.GetAllocType();126}127 128static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {129 Decorator d;130 InternalScopedString str;131 str.Append(d.Location());132 switch (descr.access_type) {133 case kAccessTypeLeft:134 str.AppendF("%p is located %zd bytes before", (void *)descr.bad_addr,135 descr.offset);136 break;137 case kAccessTypeRight:138 str.AppendF("%p is located %zd bytes after", (void *)descr.bad_addr,139 descr.offset);140 break;141 case kAccessTypeInside:142 str.AppendF("%p is located %zd bytes inside of", (void *)descr.bad_addr,143 descr.offset);144 break;145 case kAccessTypeUnknown:146 str.AppendF(147 "%p is located somewhere around (this is AddressSanitizer bug!)",148 (void *)descr.bad_addr);149 }150 str.AppendF(" %zu-byte region [%p,%p)\n", descr.chunk_size,151 (void *)descr.chunk_begin,152 (void *)(descr.chunk_begin + descr.chunk_size));153 str.Append(d.Default());154 Printf("%s", str.data());155}156 157bool GetHeapAddressInformation(uptr addr, uptr access_size,158 HeapAddressDescription *descr) {159 AsanChunkView chunk = FindHeapChunkByAddress(addr);160 if (!chunk.IsValid()) {161 return false;162 }163 descr->addr = addr;164 GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,165 access_size);166 CHECK_NE(chunk.AllocTid(), kInvalidTid);167 descr->alloc_tid = chunk.AllocTid();168 descr->alloc_stack_id = chunk.GetAllocStackId();169 descr->free_tid = chunk.FreeTid();170 if (descr->free_tid != kInvalidTid)171 descr->free_stack_id = chunk.GetFreeStackId();172 return true;173}174 175static StackTrace GetStackTraceFromId(u32 id) {176 CHECK(id);177 StackTrace res = StackDepotGet(id);178 CHECK(res.trace);179 return res;180}181 182bool DescribeAddressIfHeap(uptr addr, uptr access_size) {183 HeapAddressDescription descr;184 if (!GetHeapAddressInformation(addr, access_size, &descr)) {185 Printf(186 "AddressSanitizer can not describe address in more detail "187 "(wild memory access suspected).\n");188 return false;189 }190 descr.Print();191 return true;192}193 194// Stack descriptions195bool GetStackAddressInformation(uptr addr, uptr access_size,196 StackAddressDescription *descr) {197 AsanThread *t = FindThreadByStackAddress(addr);198 if (!t) return false;199 200 descr->addr = addr;201 descr->tid = t->tid();202 // Try to fetch precise stack frame for this access.203 AsanThread::StackFrameAccess access;204 if (!t->GetStackFrameAccessByAddr(addr, &access)) {205 descr->frame_descr = nullptr;206 return true;207 }208 209 descr->offset = access.offset;210 descr->access_size = access_size;211 descr->frame_pc = access.frame_pc;212 descr->frame_descr = access.frame_descr;213 214#if SANITIZER_PPC64V1 || SANITIZER_AIX215 // On PowerPC64 ELFv1 or AIX, the address of a function actually points to a216 // three-doubleword (or three-word for 32-bit AIX) data structure with217 // the first field containing the address of the function's code.218 descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);219#endif220 descr->frame_pc += 16;221 222 return true;223}224 225static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,226 uptr access_size, uptr prev_var_end,227 uptr next_var_beg) {228 uptr var_end = var.beg + var.size;229 uptr addr_end = addr + access_size;230 const char *pos_descr = nullptr;231 // If the variable [var.beg, var_end) is the nearest variable to the232 // current memory access, indicate it in the log.233 if (addr >= var.beg) {234 if (addr_end <= var_end)235 pos_descr = "is inside"; // May happen if this is a use-after-return.236 else if (addr < var_end)237 pos_descr = "partially overflows";238 else if (addr_end <= next_var_beg &&239 next_var_beg - addr_end >= addr - var_end)240 pos_descr = "overflows";241 } else {242 if (addr_end > var.beg)243 pos_descr = "partially underflows";244 else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)245 pos_descr = "underflows";246 }247 InternalScopedString str;248 str.AppendF(" [%zd, %zd)", var.beg, var_end);249 // Render variable name.250 str.Append(" '");251 for (uptr i = 0; i < var.name_len; ++i) {252 str.AppendF("%c", var.name_pos[i]);253 }254 str.Append("'");255 if (var.line > 0) {256 str.AppendF(" (line %zd)", var.line);257 }258 if (pos_descr) {259 Decorator d;260 // FIXME: we may want to also print the size of the access here,261 // but in case of accesses generated by memset it may be confusing.262 str.AppendF("%s <== Memory access at offset %zd %s this variable%s\n",263 d.Location(), addr, pos_descr, d.Default());264 } else {265 str.Append("\n");266 }267 Printf("%s", str.data());268}269 270bool DescribeAddressIfStack(uptr addr, uptr access_size) {271 StackAddressDescription descr;272 if (!GetStackAddressInformation(addr, access_size, &descr)) return false;273 descr.Print();274 return true;275}276 277// Global descriptions278static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,279 const __asan_global &g) {280 InternalScopedString str;281 Decorator d;282 str.Append(d.Location());283 if (addr < g.beg) {284 str.AppendF("%p is located %zd bytes before", (void *)addr, g.beg - addr);285 } else if (addr + access_size > g.beg + g.size) {286 if (addr < g.beg + g.size) addr = g.beg + g.size;287 str.AppendF("%p is located %zd bytes after", (void *)addr,288 addr - (g.beg + g.size));289 } else {290 // Can it happen?291 str.AppendF("%p is located %zd bytes inside of", (void *)addr,292 addr - g.beg);293 }294 str.AppendF(" global variable '%s' defined in '",295 MaybeDemangleGlobalName(g.name));296 PrintGlobalLocation(&str, g, /*print_module_name=*/false);297 str.AppendF("' (%p) of size %zu\n", (void *)g.beg, g.size);298 str.Append(d.Default());299 PrintGlobalNameIfASCII(&str, g);300 Printf("%s", str.data());301}302 303bool GetGlobalAddressInformation(uptr addr, uptr access_size,304 GlobalAddressDescription *descr) {305 descr->addr = addr;306 int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,307 ARRAY_SIZE(descr->globals));308 descr->size = globals_num;309 descr->access_size = access_size;310 return globals_num != 0;311}312 313bool DescribeAddressIfGlobal(uptr addr, uptr access_size,314 const char *bug_type) {315 GlobalAddressDescription descr;316 if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;317 318 descr.Print(bug_type);319 return true;320}321 322void ShadowAddressDescription::Print() const {323 Printf("Address %p is located in the %s area.\n", (void *)addr,324 ShadowNames[kind]);325}326 327void GlobalAddressDescription::Print(const char *bug_type) const {328 for (int i = 0; i < size; i++) {329 DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);330 if (bug_type &&331 0 == internal_strcmp(bug_type, "initialization-order-fiasco") &&332 reg_sites[i]) {333 Printf(" registered at:\n");334 StackDepotGet(reg_sites[i]).Print();335 }336 }337}338 339bool GlobalAddressDescription::PointsInsideTheSameVariable(340 const GlobalAddressDescription &other) const {341 if (size == 0 || other.size == 0) return false;342 343 for (uptr i = 0; i < size; i++) {344 const __asan_global &a = globals[i];345 for (uptr j = 0; j < other.size; j++) {346 const __asan_global &b = other.globals[j];347 if (a.beg == b.beg &&348 a.beg <= addr &&349 b.beg <= other.addr &&350 (addr + access_size) < (a.beg + a.size) &&351 (other.addr + other.access_size) < (b.beg + b.size))352 return true;353 }354 }355 356 return false;357}358 359void StackAddressDescription::Print() const {360 Decorator d;361 Printf("%s", d.Location());362 Printf("Address %p is located in stack of thread %s", (void *)addr,363 AsanThreadIdAndName(tid).c_str());364 365 if (!frame_descr) {366 Printf("%s\n", d.Default());367 return;368 }369 Printf(" at offset %zu in frame%s\n", offset, d.Default());370 371 // Now we print the frame where the alloca has happened.372 // We print this frame as a stack trace with one element.373 // The symbolizer may print more than one frame if inlining was involved.374 // The frame numbers may be different than those in the stack trace printed375 // previously. That's unfortunate, but I have no better solution,376 // especially given that the alloca may be from entirely different place377 // (e.g. use-after-scope, or different thread's stack).378 Printf("%s", d.Default());379 StackTrace alloca_stack(&frame_pc, 1);380 alloca_stack.Print();381 382 InternalMmapVector<StackVarDescr> vars;383 vars.reserve(16);384 if (!ParseFrameDescription(frame_descr, &vars)) {385 Printf(386 "AddressSanitizer can't parse the stack frame "387 "descriptor: |%s|\n",388 frame_descr);389 // 'addr' is a stack address, so return true even if we can't parse frame390 return;391 }392 uptr n_objects = vars.size();393 // Report the number of stack objects.394 Printf(" This frame has %zu object(s):\n", n_objects);395 396 // Report all objects in this frame.397 for (uptr i = 0; i < n_objects; i++) {398 uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;399 uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);400 PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,401 next_var_beg);402 }403 Printf(404 "HINT: this may be a false positive if your program uses "405 "some custom stack unwind mechanism, swapcontext or vfork\n");406 if (SANITIZER_WINDOWS)407 Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");408 else409 Printf(" (longjmp and C++ exceptions *are* supported)\n");410 411 DescribeThread(GetThreadContextByTidLocked(tid));412}413 414void HeapAddressDescription::Print() const {415 PrintHeapChunkAccess(addr, chunk_access);416 417 asanThreadRegistry().CheckLocked();418 AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);419 StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);420 421 Decorator d;422 AsanThreadContext *free_thread = nullptr;423 if (free_tid != kInvalidTid) {424 free_thread = GetThreadContextByTidLocked(free_tid);425 Printf("%sfreed by thread %s here:%s\n", d.Allocation(),426 AsanThreadIdAndName(free_thread).c_str(), d.Default());427 StackTrace free_stack = GetStackTraceFromId(free_stack_id);428 free_stack.Print();429 Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(),430 AsanThreadIdAndName(alloc_thread).c_str(), d.Default());431 } else {432 Printf("%sallocated by thread %s here:%s\n", d.Allocation(),433 AsanThreadIdAndName(alloc_thread).c_str(), d.Default());434 }435 alloc_stack.Print();436 DescribeThread(GetCurrentThread());437 if (free_thread) DescribeThread(free_thread);438 DescribeThread(alloc_thread);439}440 441AddressDescription::AddressDescription(uptr addr, uptr access_size,442 bool shouldLockThreadRegistry) {443 if (GetShadowAddressInformation(addr, &data.shadow)) {444 data.kind = kAddressKindShadow;445 return;446 }447 448 // Check global first. On AIX, some global data defined in shared libraries449 // are put to the STACK region for unknown reasons. Check global first can450 // workaround this issue.451 // TODO: Look into whether there's a different solution to this problem.452#if SANITIZER_AIX453 if (GetGlobalAddressInformation(addr, access_size, &data.global)) {454 data.kind = kAddressKindGlobal;455 return;456 }457#endif458 459 if (GetHeapAddressInformation(addr, access_size, &data.heap)) {460 data.kind = kAddressKindHeap;461 return;462 }463 464 bool isStackMemory = false;465 if (shouldLockThreadRegistry) {466 ThreadRegistryLock l(&asanThreadRegistry());467 isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);468 } else {469 isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);470 }471 if (isStackMemory) {472 data.kind = kAddressKindStack;473 return;474 }475 476// GetGlobalAddressInformation is called earlier on AIX due to a workaround477#if !SANITIZER_AIX478 if (GetGlobalAddressInformation(addr, access_size, &data.global)) {479 data.kind = kAddressKindGlobal;480 return;481 }482#endif483 484 data.kind = kAddressKindWild;485 data.wild.addr = addr;486 data.wild.access_size = access_size;487}488 489void WildAddressDescription::Print() const {490 Printf("Address %p is a wild pointer inside of access range of size %p.\n",491 (void *)addr, (void *)access_size);492}493 494void PrintAddressDescription(uptr addr, uptr access_size,495 const char *bug_type) {496 ShadowAddressDescription shadow_descr;497 if (GetShadowAddressInformation(addr, &shadow_descr)) {498 shadow_descr.Print();499 return;500 }501 502 GlobalAddressDescription global_descr;503 if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {504 global_descr.Print(bug_type);505 return;506 }507 508 StackAddressDescription stack_descr;509 if (GetStackAddressInformation(addr, access_size, &stack_descr)) {510 stack_descr.Print();511 return;512 }513 514 HeapAddressDescription heap_descr;515 if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {516 heap_descr.Print();517 return;518 }519 520 // We exhausted our possibilities. Bail out.521 Printf(522 "AddressSanitizer can not describe address in more detail "523 "(wild memory access suspected).\n");524}525} // namespace __asan526