Path: blob/main/contrib/llvm-project/compiler-rt/lib/asan/asan_descriptions.cpp
35233 views
//===-- asan_descriptions.cpp -----------------------------------*- C++ -*-===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7//8// This file is a part of AddressSanitizer, an address sanity checker.9//10// ASan functions for getting information about an address and/or printing it.11//===----------------------------------------------------------------------===//1213#include "asan_descriptions.h"14#include "asan_mapping.h"15#include "asan_report.h"16#include "asan_stack.h"17#include "sanitizer_common/sanitizer_stackdepot.h"1819namespace __asan {2021AsanThreadIdAndName::AsanThreadIdAndName(AsanThreadContext *t) {22Init(t->tid, t->name);23}2425AsanThreadIdAndName::AsanThreadIdAndName(u32 tid) {26if (tid == kInvalidTid) {27Init(tid, "");28} else {29asanThreadRegistry().CheckLocked();30AsanThreadContext *t = GetThreadContextByTidLocked(tid);31Init(tid, t->name);32}33}3435void AsanThreadIdAndName::Init(u32 tid, const char *tname) {36int len = internal_snprintf(name, sizeof(name), "T%d", tid);37CHECK(((unsigned int)len) < sizeof(name));38if (tname[0] != '\0')39internal_snprintf(&name[len], sizeof(name) - len, " (%s)", tname);40}4142void DescribeThread(AsanThreadContext *context) {43CHECK(context);44asanThreadRegistry().CheckLocked();45// No need to announce the main thread.46if (context->tid == kMainTid || context->announced) {47return;48}49context->announced = true;50InternalScopedString str;51str.AppendF("Thread %s", AsanThreadIdAndName(context).c_str());52if (context->parent_tid == kInvalidTid) {53str.Append(" created by unknown thread\n");54Printf("%s", str.data());55return;56}57str.AppendF(" created by %s here:\n",58AsanThreadIdAndName(context->parent_tid).c_str());59Printf("%s", str.data());60StackDepotGet(context->stack_id).Print();61// Recursively described parent thread if needed.62if (flags()->print_full_thread_history) {63AsanThreadContext *parent_context =64GetThreadContextByTidLocked(context->parent_tid);65DescribeThread(parent_context);66}67}6869// Shadow descriptions70static bool GetShadowKind(uptr addr, ShadowKind *shadow_kind) {71CHECK(!AddrIsInMem(addr));72if (AddrIsInShadowGap(addr)) {73*shadow_kind = kShadowKindGap;74} else if (AddrIsInHighShadow(addr)) {75*shadow_kind = kShadowKindHigh;76} else if (AddrIsInLowShadow(addr)) {77*shadow_kind = kShadowKindLow;78} else {79return false;80}81return true;82}8384bool DescribeAddressIfShadow(uptr addr) {85ShadowAddressDescription descr;86if (!GetShadowAddressInformation(addr, &descr)) return false;87descr.Print();88return true;89}9091bool GetShadowAddressInformation(uptr addr, ShadowAddressDescription *descr) {92if (AddrIsInMem(addr)) return false;93ShadowKind shadow_kind;94if (!GetShadowKind(addr, &shadow_kind)) return false;95if (shadow_kind != kShadowKindGap) descr->shadow_byte = *(u8 *)addr;96descr->addr = addr;97descr->kind = shadow_kind;98return true;99}100101// Heap descriptions102static void GetAccessToHeapChunkInformation(ChunkAccess *descr,103AsanChunkView chunk, uptr addr,104uptr access_size) {105descr->bad_addr = addr;106if (chunk.AddrIsAtLeft(addr, access_size, &descr->offset)) {107descr->access_type = kAccessTypeLeft;108} else if (chunk.AddrIsAtRight(addr, access_size, &descr->offset)) {109descr->access_type = kAccessTypeRight;110if (descr->offset < 0) {111descr->bad_addr -= descr->offset;112descr->offset = 0;113}114} else if (chunk.AddrIsInside(addr, access_size, &descr->offset)) {115descr->access_type = kAccessTypeInside;116} else {117descr->access_type = kAccessTypeUnknown;118}119descr->chunk_begin = chunk.Beg();120descr->chunk_size = chunk.UsedSize();121descr->user_requested_alignment = chunk.UserRequestedAlignment();122descr->alloc_type = chunk.GetAllocType();123}124125static void PrintHeapChunkAccess(uptr addr, const ChunkAccess &descr) {126Decorator d;127InternalScopedString str;128str.Append(d.Location());129switch (descr.access_type) {130case kAccessTypeLeft:131str.AppendF("%p is located %zd bytes before", (void *)descr.bad_addr,132descr.offset);133break;134case kAccessTypeRight:135str.AppendF("%p is located %zd bytes after", (void *)descr.bad_addr,136descr.offset);137break;138case kAccessTypeInside:139str.AppendF("%p is located %zd bytes inside of", (void *)descr.bad_addr,140descr.offset);141break;142case kAccessTypeUnknown:143str.AppendF(144"%p is located somewhere around (this is AddressSanitizer bug!)",145(void *)descr.bad_addr);146}147str.AppendF(" %zu-byte region [%p,%p)\n", descr.chunk_size,148(void *)descr.chunk_begin,149(void *)(descr.chunk_begin + descr.chunk_size));150str.Append(d.Default());151Printf("%s", str.data());152}153154bool GetHeapAddressInformation(uptr addr, uptr access_size,155HeapAddressDescription *descr) {156AsanChunkView chunk = FindHeapChunkByAddress(addr);157if (!chunk.IsValid()) {158return false;159}160descr->addr = addr;161GetAccessToHeapChunkInformation(&descr->chunk_access, chunk, addr,162access_size);163CHECK_NE(chunk.AllocTid(), kInvalidTid);164descr->alloc_tid = chunk.AllocTid();165descr->alloc_stack_id = chunk.GetAllocStackId();166descr->free_tid = chunk.FreeTid();167if (descr->free_tid != kInvalidTid)168descr->free_stack_id = chunk.GetFreeStackId();169return true;170}171172static StackTrace GetStackTraceFromId(u32 id) {173CHECK(id);174StackTrace res = StackDepotGet(id);175CHECK(res.trace);176return res;177}178179bool DescribeAddressIfHeap(uptr addr, uptr access_size) {180HeapAddressDescription descr;181if (!GetHeapAddressInformation(addr, access_size, &descr)) {182Printf(183"AddressSanitizer can not describe address in more detail "184"(wild memory access suspected).\n");185return false;186}187descr.Print();188return true;189}190191// Stack descriptions192bool GetStackAddressInformation(uptr addr, uptr access_size,193StackAddressDescription *descr) {194AsanThread *t = FindThreadByStackAddress(addr);195if (!t) return false;196197descr->addr = addr;198descr->tid = t->tid();199// Try to fetch precise stack frame for this access.200AsanThread::StackFrameAccess access;201if (!t->GetStackFrameAccessByAddr(addr, &access)) {202descr->frame_descr = nullptr;203return true;204}205206descr->offset = access.offset;207descr->access_size = access_size;208descr->frame_pc = access.frame_pc;209descr->frame_descr = access.frame_descr;210211#if SANITIZER_PPC64V1212// On PowerPC64 ELFv1, the address of a function actually points to a213// three-doubleword data structure with the first field containing214// the address of the function's code.215descr->frame_pc = *reinterpret_cast<uptr *>(descr->frame_pc);216#endif217descr->frame_pc += 16;218219return true;220}221222static void PrintAccessAndVarIntersection(const StackVarDescr &var, uptr addr,223uptr access_size, uptr prev_var_end,224uptr next_var_beg) {225uptr var_end = var.beg + var.size;226uptr addr_end = addr + access_size;227const char *pos_descr = nullptr;228// If the variable [var.beg, var_end) is the nearest variable to the229// current memory access, indicate it in the log.230if (addr >= var.beg) {231if (addr_end <= var_end)232pos_descr = "is inside"; // May happen if this is a use-after-return.233else if (addr < var_end)234pos_descr = "partially overflows";235else if (addr_end <= next_var_beg &&236next_var_beg - addr_end >= addr - var_end)237pos_descr = "overflows";238} else {239if (addr_end > var.beg)240pos_descr = "partially underflows";241else if (addr >= prev_var_end && addr - prev_var_end >= var.beg - addr_end)242pos_descr = "underflows";243}244InternalScopedString str;245str.AppendF(" [%zd, %zd)", var.beg, var_end);246// Render variable name.247str.Append(" '");248for (uptr i = 0; i < var.name_len; ++i) {249str.AppendF("%c", var.name_pos[i]);250}251str.Append("'");252if (var.line > 0) {253str.AppendF(" (line %zd)", var.line);254}255if (pos_descr) {256Decorator d;257// FIXME: we may want to also print the size of the access here,258// but in case of accesses generated by memset it may be confusing.259str.AppendF("%s <== Memory access at offset %zd %s this variable%s\n",260d.Location(), addr, pos_descr, d.Default());261} else {262str.Append("\n");263}264Printf("%s", str.data());265}266267bool DescribeAddressIfStack(uptr addr, uptr access_size) {268StackAddressDescription descr;269if (!GetStackAddressInformation(addr, access_size, &descr)) return false;270descr.Print();271return true;272}273274// Global descriptions275static void DescribeAddressRelativeToGlobal(uptr addr, uptr access_size,276const __asan_global &g) {277InternalScopedString str;278Decorator d;279str.Append(d.Location());280if (addr < g.beg) {281str.AppendF("%p is located %zd bytes before", (void *)addr, g.beg - addr);282} else if (addr + access_size > g.beg + g.size) {283if (addr < g.beg + g.size) addr = g.beg + g.size;284str.AppendF("%p is located %zd bytes after", (void *)addr,285addr - (g.beg + g.size));286} else {287// Can it happen?288str.AppendF("%p is located %zd bytes inside of", (void *)addr,289addr - g.beg);290}291str.AppendF(" global variable '%s' defined in '",292MaybeDemangleGlobalName(g.name));293PrintGlobalLocation(&str, g, /*print_module_name=*/false);294str.AppendF("' (%p) of size %zu\n", (void *)g.beg, g.size);295str.Append(d.Default());296PrintGlobalNameIfASCII(&str, g);297Printf("%s", str.data());298}299300bool GetGlobalAddressInformation(uptr addr, uptr access_size,301GlobalAddressDescription *descr) {302descr->addr = addr;303int globals_num = GetGlobalsForAddress(addr, descr->globals, descr->reg_sites,304ARRAY_SIZE(descr->globals));305descr->size = globals_num;306descr->access_size = access_size;307return globals_num != 0;308}309310bool DescribeAddressIfGlobal(uptr addr, uptr access_size,311const char *bug_type) {312GlobalAddressDescription descr;313if (!GetGlobalAddressInformation(addr, access_size, &descr)) return false;314315descr.Print(bug_type);316return true;317}318319void ShadowAddressDescription::Print() const {320Printf("Address %p is located in the %s area.\n", (void *)addr,321ShadowNames[kind]);322}323324void GlobalAddressDescription::Print(const char *bug_type) const {325for (int i = 0; i < size; i++) {326DescribeAddressRelativeToGlobal(addr, access_size, globals[i]);327if (bug_type &&3280 == internal_strcmp(bug_type, "initialization-order-fiasco") &&329reg_sites[i]) {330Printf(" registered at:\n");331StackDepotGet(reg_sites[i]).Print();332}333}334}335336bool GlobalAddressDescription::PointsInsideTheSameVariable(337const GlobalAddressDescription &other) const {338if (size == 0 || other.size == 0) return false;339340for (uptr i = 0; i < size; i++) {341const __asan_global &a = globals[i];342for (uptr j = 0; j < other.size; j++) {343const __asan_global &b = other.globals[j];344if (a.beg == b.beg &&345a.beg <= addr &&346b.beg <= other.addr &&347(addr + access_size) < (a.beg + a.size) &&348(other.addr + other.access_size) < (b.beg + b.size))349return true;350}351}352353return false;354}355356void StackAddressDescription::Print() const {357Decorator d;358Printf("%s", d.Location());359Printf("Address %p is located in stack of thread %s", (void *)addr,360AsanThreadIdAndName(tid).c_str());361362if (!frame_descr) {363Printf("%s\n", d.Default());364return;365}366Printf(" at offset %zu in frame%s\n", offset, d.Default());367368// Now we print the frame where the alloca has happened.369// We print this frame as a stack trace with one element.370// The symbolizer may print more than one frame if inlining was involved.371// The frame numbers may be different than those in the stack trace printed372// previously. That's unfortunate, but I have no better solution,373// especially given that the alloca may be from entirely different place374// (e.g. use-after-scope, or different thread's stack).375Printf("%s", d.Default());376StackTrace alloca_stack(&frame_pc, 1);377alloca_stack.Print();378379InternalMmapVector<StackVarDescr> vars;380vars.reserve(16);381if (!ParseFrameDescription(frame_descr, &vars)) {382Printf(383"AddressSanitizer can't parse the stack frame "384"descriptor: |%s|\n",385frame_descr);386// 'addr' is a stack address, so return true even if we can't parse frame387return;388}389uptr n_objects = vars.size();390// Report the number of stack objects.391Printf(" This frame has %zu object(s):\n", n_objects);392393// Report all objects in this frame.394for (uptr i = 0; i < n_objects; i++) {395uptr prev_var_end = i ? vars[i - 1].beg + vars[i - 1].size : 0;396uptr next_var_beg = i + 1 < n_objects ? vars[i + 1].beg : ~(0UL);397PrintAccessAndVarIntersection(vars[i], offset, access_size, prev_var_end,398next_var_beg);399}400Printf(401"HINT: this may be a false positive if your program uses "402"some custom stack unwind mechanism, swapcontext or vfork\n");403if (SANITIZER_WINDOWS)404Printf(" (longjmp, SEH and C++ exceptions *are* supported)\n");405else406Printf(" (longjmp and C++ exceptions *are* supported)\n");407408DescribeThread(GetThreadContextByTidLocked(tid));409}410411void HeapAddressDescription::Print() const {412PrintHeapChunkAccess(addr, chunk_access);413414asanThreadRegistry().CheckLocked();415AsanThreadContext *alloc_thread = GetThreadContextByTidLocked(alloc_tid);416StackTrace alloc_stack = GetStackTraceFromId(alloc_stack_id);417418Decorator d;419AsanThreadContext *free_thread = nullptr;420if (free_tid != kInvalidTid) {421free_thread = GetThreadContextByTidLocked(free_tid);422Printf("%sfreed by thread %s here:%s\n", d.Allocation(),423AsanThreadIdAndName(free_thread).c_str(), d.Default());424StackTrace free_stack = GetStackTraceFromId(free_stack_id);425free_stack.Print();426Printf("%spreviously allocated by thread %s here:%s\n", d.Allocation(),427AsanThreadIdAndName(alloc_thread).c_str(), d.Default());428} else {429Printf("%sallocated by thread %s here:%s\n", d.Allocation(),430AsanThreadIdAndName(alloc_thread).c_str(), d.Default());431}432alloc_stack.Print();433DescribeThread(GetCurrentThread());434if (free_thread) DescribeThread(free_thread);435DescribeThread(alloc_thread);436}437438AddressDescription::AddressDescription(uptr addr, uptr access_size,439bool shouldLockThreadRegistry) {440if (GetShadowAddressInformation(addr, &data.shadow)) {441data.kind = kAddressKindShadow;442return;443}444if (GetHeapAddressInformation(addr, access_size, &data.heap)) {445data.kind = kAddressKindHeap;446return;447}448449bool isStackMemory = false;450if (shouldLockThreadRegistry) {451ThreadRegistryLock l(&asanThreadRegistry());452isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);453} else {454isStackMemory = GetStackAddressInformation(addr, access_size, &data.stack);455}456if (isStackMemory) {457data.kind = kAddressKindStack;458return;459}460461if (GetGlobalAddressInformation(addr, access_size, &data.global)) {462data.kind = kAddressKindGlobal;463return;464}465data.kind = kAddressKindWild;466data.wild.addr = addr;467data.wild.access_size = access_size;468}469470void WildAddressDescription::Print() const {471Printf("Address %p is a wild pointer inside of access range of size %p.\n",472(void *)addr, (void *)access_size);473}474475void PrintAddressDescription(uptr addr, uptr access_size,476const char *bug_type) {477ShadowAddressDescription shadow_descr;478if (GetShadowAddressInformation(addr, &shadow_descr)) {479shadow_descr.Print();480return;481}482483GlobalAddressDescription global_descr;484if (GetGlobalAddressInformation(addr, access_size, &global_descr)) {485global_descr.Print(bug_type);486return;487}488489StackAddressDescription stack_descr;490if (GetStackAddressInformation(addr, access_size, &stack_descr)) {491stack_descr.Print();492return;493}494495HeapAddressDescription heap_descr;496if (GetHeapAddressInformation(addr, access_size, &heap_descr)) {497heap_descr.Print();498return;499}500501// We exhausted our possibilities. Bail out.502Printf(503"AddressSanitizer can not describe address in more detail "504"(wild memory access suspected).\n");505}506} // namespace __asan507508509