345 lines · cpp
1//===-- sanitizer_symbolizer_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-exception6//7//===----------------------------------------------------------------------===//8///9/// This file is shared between AddressSanitizer and other sanitizer run-time10/// libraries and implements symbolized reports related functions.11///12//===----------------------------------------------------------------------===//13 14#include "sanitizer_common.h"15#include "sanitizer_file.h"16#include "sanitizer_flags.h"17#include "sanitizer_procmaps.h"18#include "sanitizer_report_decorator.h"19#include "sanitizer_stacktrace.h"20#include "sanitizer_stacktrace_printer.h"21#include "sanitizer_symbolizer.h"22 23#if SANITIZER_POSIX24# include "sanitizer_posix.h"25# include <sys/mman.h>26#endif27 28namespace __sanitizer {29 30#if !SANITIZER_GO31 32static bool FrameIsInternal(const SymbolizedStack *frame) {33 if (!frame)34 return true;35 const char *file = frame->info.file;36 const char *module = frame->info.module;37 // On Gentoo, the path is g++-*, so there's *not* a missing /.38 if (file && (internal_strstr(file, "/compiler-rt/lib/") ||39 internal_strstr(file, "/include/c++/") ||40 internal_strstr(file, "/include/g++")))41 return true;42 if (file && internal_strstr(file, "\\compiler-rt\\lib\\"))43 return true;44 if (module && (internal_strstr(module, "libclang_rt.")))45 return true;46 if (module && (internal_strstr(module, "clang_rt.")))47 return true;48 return false;49}50 51const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) {52 for (const SymbolizedStack *f = frames; f; f = f->next)53 if (!FrameIsInternal(f))54 return f;55 return nullptr;56}57 58void ReportErrorSummary(const char *error_type, const AddressInfo &info,59 const char *alt_tool_name) {60 if (!common_flags()->print_summary) return;61 InternalScopedString buff;62 buff.AppendF("%s ", error_type);63 StackTracePrinter::GetOrInit()->RenderFrame(64 &buff, "%L %F", 0, info.address, &info,65 common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix);66 ReportErrorSummary(buff.data(), alt_tool_name);67}68#endif69 70#if !SANITIZER_FUCHSIA71 72bool ReportFile::SupportsColors() {73 SpinMutexLock l(mu);74 ReopenIfNecessary();75 return SupportsColoredOutput(fd);76}77 78static inline bool ReportSupportsColors() {79 return report_file.SupportsColors();80}81 82#else // SANITIZER_FUCHSIA83 84// Fuchsia's logs always go through post-processing that handles colorization.85static inline bool ReportSupportsColors() { return true; }86 87#endif // !SANITIZER_FUCHSIA88 89bool ColorizeReports() {90 // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color91 // printing on Windows.92 if (SANITIZER_WINDOWS)93 return false;94 95 const char *flag = common_flags()->color;96 return internal_strcmp(flag, "always") == 0 ||97 (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors());98}99 100void ReportErrorSummary(const char *error_type, const StackTrace *stack,101 const char *alt_tool_name) {102#if !SANITIZER_GO103 if (!common_flags()->print_summary)104 return;105 106 // Find first non-internal stack frame.107 for (uptr i = 0; i < stack->size; ++i) {108 uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[i]);109 SymbolizedStackHolder symbolized_stack(110 Symbolizer::GetOrInit()->SymbolizePC(pc));111 if (const SymbolizedStack *frame = symbolized_stack.get()) {112 if (const SymbolizedStack *summary_frame = SkipInternalFrames(frame)) {113 ReportErrorSummary(error_type, summary_frame->info, alt_tool_name);114 return;115 }116 }117 }118 119 // Fallback to the top one.120 if (stack->size) {121 uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);122 SymbolizedStackHolder symbolized_stack(123 Symbolizer::GetOrInit()->SymbolizePC(pc));124 if (const SymbolizedStack *frame = symbolized_stack.get()) {125 ReportErrorSummary(error_type, frame->info, alt_tool_name);126 return;127 }128 }129 130 // Fallback to a summary without location.131 ReportErrorSummary(error_type);132#endif133}134 135void ReportMmapWriteExec(int prot, int flags) {136#if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID)137 int pflags = (PROT_WRITE | PROT_EXEC);138 if ((prot & pflags) != pflags)139 return;140 141# if SANITIZER_APPLE && defined(MAP_JIT)142 if ((flags & MAP_JIT) == MAP_JIT)143 return;144# endif145 146 ScopedErrorReportLock l;147 SanitizerCommonDecorator d;148 149 InternalMmapVector<BufferedStackTrace> stack_buffer(1);150 BufferedStackTrace *stack = stack_buffer.data();151 stack->Reset();152 uptr top = 0;153 uptr bottom = 0;154 GET_CALLER_PC_BP;155 bool fast = common_flags()->fast_unwind_on_fatal;156 if (StackTrace::WillUseFastUnwind(fast)) {157 GetThreadStackTopAndBottom(false, &top, &bottom);158 stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, true);159 } else {160 stack->Unwind(kStackTraceMax, pc, 0, nullptr, 0, 0, false);161 }162 163 Printf("%s", d.Warning());164 Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName);165 Printf("%s", d.Default());166 167 stack->Print();168 ReportErrorSummary("w-and-x-usage", stack);169#endif170}171 172#if !SANITIZER_FUCHSIA && !SANITIZER_GO173void StartReportDeadlySignal() {174 // Write the first message using fd=2, just in case.175 // It may actually fail to write in case stderr is closed.176 CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName));177 static const char kDeadlySignal[] = ":DEADLYSIGNAL\n";178 CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1);179}180 181static void MaybeReportNonExecRegion(uptr pc) {182#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD183 MemoryMappingLayout proc_maps(/*cache_enabled*/ true);184 MemoryMappedSegment segment;185 while (proc_maps.Next(&segment)) {186 if (pc >= segment.start && pc < segment.end && !segment.IsExecutable())187 Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n");188 }189#endif190}191 192static void PrintMemoryByte(InternalScopedString *str, const char *before,193 u8 byte) {194 SanitizerCommonDecorator d;195 str->AppendF("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15,196 d.Default());197}198 199static void MaybeDumpInstructionBytes(uptr pc) {200 if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))201 return;202 InternalScopedString str;203 str.AppendF("First 16 instruction bytes at pc: ");204 if (IsAccessibleMemoryRange(pc, 16)) {205 for (int i = 0; i < 16; ++i) {206 PrintMemoryByte(&str, "", ((u8 *)pc)[i]);207 }208 str.AppendF("\n");209 } else {210 str.AppendF("unaccessible\n");211 }212 Report("%s", str.data());213}214 215static void MaybeDumpRegisters(void *context) {216 if (!common_flags()->dump_registers) return;217 SignalContext::DumpAllRegisters(context);218}219 220static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid,221 UnwindSignalStackCallbackType unwind,222 const void *unwind_context) {223 SanitizerCommonDecorator d;224 Printf("%s", d.Warning());225 static const char kDescription[] = "stack-overflow";226 Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n",227 SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc,228 (void *)sig.bp, (void *)sig.sp, tid);229 Printf("%s", d.Default());230 // Avoid SEGVs in the unwinder when bp couldn't be determined.231 if (sig.bp) {232 InternalMmapVector<BufferedStackTrace> stack_buffer(1);233 BufferedStackTrace *stack = stack_buffer.data();234 stack->Reset();235 unwind(sig, unwind_context, stack);236 stack->Print();237 ReportErrorSummary(kDescription, stack);238 }239}240 241static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,242 UnwindSignalStackCallbackType unwind,243 const void *unwind_context) {244 SanitizerCommonDecorator d;245 Printf("%s", d.Warning());246 const char *description = sig.Describe();247 if (sig.is_memory_access && !sig.is_true_faulting_addr)248 Report("ERROR: %s: %s on unknown address (pc %p bp %p sp %p T%d)\n",249 SanitizerToolName, description, (void *)sig.pc, (void *)sig.bp,250 (void *)sig.sp, tid);251 else252 Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n",253 SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc,254 (void *)sig.bp, (void *)sig.sp, tid);255 Printf("%s", d.Default());256 if (sig.pc < GetPageSizeCached())257 Report("Hint: pc points to the zero page.\n");258 if (sig.is_memory_access) {259 const char *access_type =260 sig.write_flag == SignalContext::Write261 ? "WRITE"262 : (sig.write_flag == SignalContext::Read ? "READ" : "UNKNOWN");263 Report("The signal is caused by a %s memory access.\n", access_type);264 if (!sig.is_true_faulting_addr)265 Report("Hint: this fault was caused by a dereference of a high value "266 "address (see register values below). Disassemble the provided "267 "pc to learn which register was used.\n");268 else if (sig.addr < GetPageSizeCached())269 Report("Hint: address points to the zero page.\n");270 }271 MaybeReportNonExecRegion(sig.pc);272 InternalMmapVector<BufferedStackTrace> stack_buffer(1);273 BufferedStackTrace *stack = stack_buffer.data();274 stack->Reset();275 unwind(sig, unwind_context, stack);276 stack->Print();277 MaybeDumpInstructionBytes(sig.pc);278 MaybeDumpRegisters(sig.context);279 Printf("%s can not provide additional info.\n", SanitizerToolName);280 ReportErrorSummary(description, stack);281}282 283void ReportDeadlySignal(const SignalContext &sig, u32 tid,284 UnwindSignalStackCallbackType unwind,285 const void *unwind_context) {286 if (sig.IsStackOverflow())287 ReportStackOverflowImpl(sig, tid, unwind, unwind_context);288 else289 ReportDeadlySignalImpl(sig, tid, unwind, unwind_context);290}291 292void HandleDeadlySignal(void *siginfo, void *context, u32 tid,293 UnwindSignalStackCallbackType unwind,294 const void *unwind_context) {295 StartReportDeadlySignal();296 ScopedErrorReportLock rl;297 SignalContext sig(siginfo, context);298 ReportDeadlySignal(sig, tid, unwind, unwind_context);299 Report("ABORTING\n");300 Die();301}302 303#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO304 305atomic_uintptr_t ScopedErrorReportLock::reporting_thread_ = {0};306StaticSpinMutex ScopedErrorReportLock::mutex_;307 308void ScopedErrorReportLock::Lock() {309 uptr current = GetThreadSelf();310 for (;;) {311 uptr expected = 0;312 if (atomic_compare_exchange_strong(&reporting_thread_, &expected, current,313 memory_order_relaxed)) {314 // We've claimed reporting_thread so proceed.315 mutex_.Lock();316 return;317 }318 319 if (expected == current) {320 // This is either asynch signal or nested error during error reporting.321 // Fail simple to avoid deadlocks in Report().322 323 // Can't use Report() here because of potential deadlocks in nested324 // signal handlers.325 CatastrophicErrorWrite(SanitizerToolName,326 internal_strlen(SanitizerToolName));327 static const char msg[] = ": nested bug in the same thread, aborting.\n";328 CatastrophicErrorWrite(msg, sizeof(msg) - 1);329 330 internal__exit(common_flags()->exitcode);331 }332 333 internal_sched_yield();334 }335}336 337void ScopedErrorReportLock::Unlock() {338 mutex_.Unlock();339 atomic_store_relaxed(&reporting_thread_, 0);340}341 342void ScopedErrorReportLock::CheckLocked() { mutex_.CheckLocked(); }343 344} // namespace __sanitizer345