168d75effSDimitry Andric //===-- sanitizer_symbolizer_report.cpp -----------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric /// 968d75effSDimitry Andric /// This file is shared between AddressSanitizer and other sanitizer run-time 1068d75effSDimitry Andric /// libraries and implements symbolized reports related functions. 1168d75effSDimitry Andric /// 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric 1468d75effSDimitry Andric #include "sanitizer_common.h" 1568d75effSDimitry Andric #include "sanitizer_file.h" 1668d75effSDimitry Andric #include "sanitizer_flags.h" 1768d75effSDimitry Andric #include "sanitizer_procmaps.h" 1868d75effSDimitry Andric #include "sanitizer_report_decorator.h" 1968d75effSDimitry Andric #include "sanitizer_stacktrace.h" 2068d75effSDimitry Andric #include "sanitizer_stacktrace_printer.h" 2168d75effSDimitry Andric #include "sanitizer_symbolizer.h" 2268d75effSDimitry Andric 2368d75effSDimitry Andric #if SANITIZER_POSIX 2468d75effSDimitry Andric # include "sanitizer_posix.h" 2568d75effSDimitry Andric # include <sys/mman.h> 2668d75effSDimitry Andric #endif 2768d75effSDimitry Andric 2868d75effSDimitry Andric namespace __sanitizer { 2968d75effSDimitry Andric 3068d75effSDimitry Andric #if !SANITIZER_GO 311db9f3b2SDimitry Andric 321db9f3b2SDimitry Andric static bool FrameIsInternal(const SymbolizedStack *frame) { 331db9f3b2SDimitry Andric if (!frame) 341db9f3b2SDimitry Andric return true; 351db9f3b2SDimitry Andric const char *file = frame->info.file; 361db9f3b2SDimitry Andric const char *module = frame->info.module; 37b3edf446SDimitry Andric // On Gentoo, the path is g++-*, so there's *not* a missing /. 387a6dacacSDimitry Andric if (file && (internal_strstr(file, "/compiler-rt/lib/") || 39b3edf446SDimitry Andric internal_strstr(file, "/include/c++/") || 40b3edf446SDimitry Andric internal_strstr(file, "/include/g++"))) 411db9f3b2SDimitry Andric return true; 42*0fca6ea1SDimitry Andric if (file && internal_strstr(file, "\\compiler-rt\\lib\\")) 43*0fca6ea1SDimitry Andric return true; 441db9f3b2SDimitry Andric if (module && (internal_strstr(module, "libclang_rt."))) 451db9f3b2SDimitry Andric return true; 46*0fca6ea1SDimitry Andric if (module && (internal_strstr(module, "clang_rt."))) 47*0fca6ea1SDimitry Andric return true; 481db9f3b2SDimitry Andric return false; 491db9f3b2SDimitry Andric } 501db9f3b2SDimitry Andric 511db9f3b2SDimitry Andric const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) { 521db9f3b2SDimitry Andric for (const SymbolizedStack *f = frames; f; f = f->next) 531db9f3b2SDimitry Andric if (!FrameIsInternal(f)) 541db9f3b2SDimitry Andric return f; 551db9f3b2SDimitry Andric return nullptr; 561db9f3b2SDimitry Andric } 571db9f3b2SDimitry Andric 5868d75effSDimitry Andric void ReportErrorSummary(const char *error_type, const AddressInfo &info, 5968d75effSDimitry Andric const char *alt_tool_name) { 6068d75effSDimitry Andric if (!common_flags()->print_summary) return; 61fe6060f1SDimitry Andric InternalScopedString buff; 625f757f3fSDimitry Andric buff.AppendF("%s ", error_type); 635f757f3fSDimitry Andric StackTracePrinter::GetOrInit()->RenderFrame( 645f757f3fSDimitry Andric &buff, "%L %F", 0, info.address, &info, 655f757f3fSDimitry Andric common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix); 6668d75effSDimitry Andric ReportErrorSummary(buff.data(), alt_tool_name); 6768d75effSDimitry Andric } 6868d75effSDimitry Andric #endif 6968d75effSDimitry Andric 7068d75effSDimitry Andric #if !SANITIZER_FUCHSIA 7168d75effSDimitry Andric 7268d75effSDimitry Andric bool ReportFile::SupportsColors() { 7368d75effSDimitry Andric SpinMutexLock l(mu); 7468d75effSDimitry Andric ReopenIfNecessary(); 7568d75effSDimitry Andric return SupportsColoredOutput(fd); 7668d75effSDimitry Andric } 7768d75effSDimitry Andric 78e8d8bef9SDimitry Andric static inline bool ReportSupportsColors() { 7968d75effSDimitry Andric return report_file.SupportsColors(); 8068d75effSDimitry Andric } 8168d75effSDimitry Andric 8268d75effSDimitry Andric #else // SANITIZER_FUCHSIA 8368d75effSDimitry Andric 8468d75effSDimitry Andric // Fuchsia's logs always go through post-processing that handles colorization. 85e8d8bef9SDimitry Andric static inline bool ReportSupportsColors() { return true; } 8668d75effSDimitry Andric 8768d75effSDimitry Andric #endif // !SANITIZER_FUCHSIA 8868d75effSDimitry Andric 8968d75effSDimitry Andric bool ColorizeReports() { 9068d75effSDimitry Andric // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color 9168d75effSDimitry Andric // printing on Windows. 9268d75effSDimitry Andric if (SANITIZER_WINDOWS) 9368d75effSDimitry Andric return false; 9468d75effSDimitry Andric 9568d75effSDimitry Andric const char *flag = common_flags()->color; 9668d75effSDimitry Andric return internal_strcmp(flag, "always") == 0 || 9768d75effSDimitry Andric (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors()); 9868d75effSDimitry Andric } 9968d75effSDimitry Andric 10068d75effSDimitry Andric void ReportErrorSummary(const char *error_type, const StackTrace *stack, 10168d75effSDimitry Andric const char *alt_tool_name) { 10268d75effSDimitry Andric #if !SANITIZER_GO 10368d75effSDimitry Andric if (!common_flags()->print_summary) 10468d75effSDimitry Andric return; 105297eecfbSDimitry Andric 106297eecfbSDimitry Andric // Find first non-internal stack frame. 107297eecfbSDimitry Andric for (uptr i = 0; i < stack->size; ++i) { 108297eecfbSDimitry Andric uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[i]); 109297eecfbSDimitry Andric SymbolizedStackHolder symbolized_stack( 110297eecfbSDimitry Andric Symbolizer::GetOrInit()->SymbolizePC(pc)); 111297eecfbSDimitry Andric if (const SymbolizedStack *frame = symbolized_stack.get()) { 112297eecfbSDimitry Andric if (const SymbolizedStack *summary_frame = SkipInternalFrames(frame)) { 113297eecfbSDimitry Andric ReportErrorSummary(error_type, summary_frame->info, alt_tool_name); 11468d75effSDimitry Andric return; 11568d75effSDimitry Andric } 116297eecfbSDimitry Andric } 117297eecfbSDimitry Andric } 118297eecfbSDimitry Andric 119297eecfbSDimitry Andric // Fallback to the top one. 120297eecfbSDimitry Andric if (stack->size) { 12168d75effSDimitry Andric uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); 1221db9f3b2SDimitry Andric SymbolizedStackHolder symbolized_stack( 1231db9f3b2SDimitry Andric Symbolizer::GetOrInit()->SymbolizePC(pc)); 124297eecfbSDimitry Andric if (const SymbolizedStack *frame = symbolized_stack.get()) { 12568d75effSDimitry Andric ReportErrorSummary(error_type, frame->info, alt_tool_name); 126297eecfbSDimitry Andric return; 127297eecfbSDimitry Andric } 128297eecfbSDimitry Andric } 129297eecfbSDimitry Andric 130297eecfbSDimitry Andric // Fallback to a summary without location. 131297eecfbSDimitry Andric ReportErrorSummary(error_type); 13268d75effSDimitry Andric #endif 13368d75effSDimitry Andric } 13468d75effSDimitry Andric 135349cc55cSDimitry Andric void ReportMmapWriteExec(int prot, int flags) { 13668d75effSDimitry Andric #if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID) 137349cc55cSDimitry Andric int pflags = (PROT_WRITE | PROT_EXEC); 138349cc55cSDimitry Andric if ((prot & pflags) != pflags) 13968d75effSDimitry Andric return; 14068d75effSDimitry Andric 14181ad6265SDimitry Andric # if SANITIZER_APPLE && defined(MAP_JIT) 142349cc55cSDimitry Andric if ((flags & MAP_JIT) == MAP_JIT) 143349cc55cSDimitry Andric return; 144349cc55cSDimitry Andric # endif 145349cc55cSDimitry Andric 14668d75effSDimitry Andric ScopedErrorReportLock l; 14768d75effSDimitry Andric SanitizerCommonDecorator d; 14868d75effSDimitry Andric 14968d75effSDimitry Andric InternalMmapVector<BufferedStackTrace> stack_buffer(1); 15068d75effSDimitry Andric BufferedStackTrace *stack = stack_buffer.data(); 15168d75effSDimitry Andric stack->Reset(); 15268d75effSDimitry Andric uptr top = 0; 15368d75effSDimitry Andric uptr bottom = 0; 15406c3fb27SDimitry Andric GET_CALLER_PC_BP; 15568d75effSDimitry Andric bool fast = common_flags()->fast_unwind_on_fatal; 15668d75effSDimitry Andric if (StackTrace::WillUseFastUnwind(fast)) { 15768d75effSDimitry Andric GetThreadStackTopAndBottom(false, &top, &bottom); 15868d75effSDimitry Andric stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, true); 15968d75effSDimitry Andric } else { 16068d75effSDimitry Andric stack->Unwind(kStackTraceMax, pc, 0, nullptr, 0, 0, false); 16168d75effSDimitry Andric } 16268d75effSDimitry Andric 16368d75effSDimitry Andric Printf("%s", d.Warning()); 16468d75effSDimitry Andric Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName); 16568d75effSDimitry Andric Printf("%s", d.Default()); 16668d75effSDimitry Andric 16768d75effSDimitry Andric stack->Print(); 16868d75effSDimitry Andric ReportErrorSummary("w-and-x-usage", stack); 16968d75effSDimitry Andric #endif 17068d75effSDimitry Andric } 17168d75effSDimitry Andric 172fe6060f1SDimitry Andric #if !SANITIZER_FUCHSIA && !SANITIZER_GO 17368d75effSDimitry Andric void StartReportDeadlySignal() { 17468d75effSDimitry Andric // Write the first message using fd=2, just in case. 17568d75effSDimitry Andric // It may actually fail to write in case stderr is closed. 17668d75effSDimitry Andric CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName)); 17768d75effSDimitry Andric static const char kDeadlySignal[] = ":DEADLYSIGNAL\n"; 17868d75effSDimitry Andric CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1); 17968d75effSDimitry Andric } 18068d75effSDimitry Andric 18168d75effSDimitry Andric static void MaybeReportNonExecRegion(uptr pc) { 18268d75effSDimitry Andric #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD 18368d75effSDimitry Andric MemoryMappingLayout proc_maps(/*cache_enabled*/ true); 18468d75effSDimitry Andric MemoryMappedSegment segment; 18568d75effSDimitry Andric while (proc_maps.Next(&segment)) { 18668d75effSDimitry Andric if (pc >= segment.start && pc < segment.end && !segment.IsExecutable()) 18768d75effSDimitry Andric Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n"); 18868d75effSDimitry Andric } 18968d75effSDimitry Andric #endif 19068d75effSDimitry Andric } 19168d75effSDimitry Andric 19268d75effSDimitry Andric static void PrintMemoryByte(InternalScopedString *str, const char *before, 19368d75effSDimitry Andric u8 byte) { 19468d75effSDimitry Andric SanitizerCommonDecorator d; 1955f757f3fSDimitry Andric str->AppendF("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15, 19668d75effSDimitry Andric d.Default()); 19768d75effSDimitry Andric } 19868d75effSDimitry Andric 19968d75effSDimitry Andric static void MaybeDumpInstructionBytes(uptr pc) { 20068d75effSDimitry Andric if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) 20168d75effSDimitry Andric return; 202fe6060f1SDimitry Andric InternalScopedString str; 2035f757f3fSDimitry Andric str.AppendF("First 16 instruction bytes at pc: "); 20468d75effSDimitry Andric if (IsAccessibleMemoryRange(pc, 16)) { 20568d75effSDimitry Andric for (int i = 0; i < 16; ++i) { 20668d75effSDimitry Andric PrintMemoryByte(&str, "", ((u8 *)pc)[i]); 20768d75effSDimitry Andric } 2085f757f3fSDimitry Andric str.AppendF("\n"); 20968d75effSDimitry Andric } else { 2105f757f3fSDimitry Andric str.AppendF("unaccessible\n"); 21168d75effSDimitry Andric } 21268d75effSDimitry Andric Report("%s", str.data()); 21368d75effSDimitry Andric } 21468d75effSDimitry Andric 21568d75effSDimitry Andric static void MaybeDumpRegisters(void *context) { 21668d75effSDimitry Andric if (!common_flags()->dump_registers) return; 21768d75effSDimitry Andric SignalContext::DumpAllRegisters(context); 21868d75effSDimitry Andric } 21968d75effSDimitry Andric 22068d75effSDimitry Andric static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid, 22168d75effSDimitry Andric UnwindSignalStackCallbackType unwind, 22268d75effSDimitry Andric const void *unwind_context) { 22368d75effSDimitry Andric SanitizerCommonDecorator d; 22468d75effSDimitry Andric Printf("%s", d.Warning()); 22568d75effSDimitry Andric static const char kDescription[] = "stack-overflow"; 22668d75effSDimitry Andric Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n", 22768d75effSDimitry Andric SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc, 22868d75effSDimitry Andric (void *)sig.bp, (void *)sig.sp, tid); 22968d75effSDimitry Andric Printf("%s", d.Default()); 23068d75effSDimitry Andric InternalMmapVector<BufferedStackTrace> stack_buffer(1); 23168d75effSDimitry Andric BufferedStackTrace *stack = stack_buffer.data(); 23268d75effSDimitry Andric stack->Reset(); 23368d75effSDimitry Andric unwind(sig, unwind_context, stack); 23468d75effSDimitry Andric stack->Print(); 23568d75effSDimitry Andric ReportErrorSummary(kDescription, stack); 23668d75effSDimitry Andric } 23768d75effSDimitry Andric 23868d75effSDimitry Andric static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid, 23968d75effSDimitry Andric UnwindSignalStackCallbackType unwind, 24068d75effSDimitry Andric const void *unwind_context) { 24168d75effSDimitry Andric SanitizerCommonDecorator d; 24268d75effSDimitry Andric Printf("%s", d.Warning()); 24368d75effSDimitry Andric const char *description = sig.Describe(); 24468d75effSDimitry Andric if (sig.is_memory_access && !sig.is_true_faulting_addr) 24568d75effSDimitry Andric Report("ERROR: %s: %s on unknown address (pc %p bp %p sp %p T%d)\n", 24668d75effSDimitry Andric SanitizerToolName, description, (void *)sig.pc, (void *)sig.bp, 24768d75effSDimitry Andric (void *)sig.sp, tid); 24868d75effSDimitry Andric else 24968d75effSDimitry Andric Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n", 25068d75effSDimitry Andric SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc, 25168d75effSDimitry Andric (void *)sig.bp, (void *)sig.sp, tid); 25268d75effSDimitry Andric Printf("%s", d.Default()); 25368d75effSDimitry Andric if (sig.pc < GetPageSizeCached()) 25468d75effSDimitry Andric Report("Hint: pc points to the zero page.\n"); 25568d75effSDimitry Andric if (sig.is_memory_access) { 25668d75effSDimitry Andric const char *access_type = 257d56accc7SDimitry Andric sig.write_flag == SignalContext::Write 25868d75effSDimitry Andric ? "WRITE" 259d56accc7SDimitry Andric : (sig.write_flag == SignalContext::Read ? "READ" : "UNKNOWN"); 26068d75effSDimitry Andric Report("The signal is caused by a %s memory access.\n", access_type); 26168d75effSDimitry Andric if (!sig.is_true_faulting_addr) 26268d75effSDimitry Andric Report("Hint: this fault was caused by a dereference of a high value " 263e8d8bef9SDimitry Andric "address (see register values below). Disassemble the provided " 26468d75effSDimitry Andric "pc to learn which register was used.\n"); 26568d75effSDimitry Andric else if (sig.addr < GetPageSizeCached()) 26668d75effSDimitry Andric Report("Hint: address points to the zero page.\n"); 26768d75effSDimitry Andric } 26868d75effSDimitry Andric MaybeReportNonExecRegion(sig.pc); 26968d75effSDimitry Andric InternalMmapVector<BufferedStackTrace> stack_buffer(1); 27068d75effSDimitry Andric BufferedStackTrace *stack = stack_buffer.data(); 27168d75effSDimitry Andric stack->Reset(); 27268d75effSDimitry Andric unwind(sig, unwind_context, stack); 27368d75effSDimitry Andric stack->Print(); 27468d75effSDimitry Andric MaybeDumpInstructionBytes(sig.pc); 27568d75effSDimitry Andric MaybeDumpRegisters(sig.context); 27668d75effSDimitry Andric Printf("%s can not provide additional info.\n", SanitizerToolName); 27768d75effSDimitry Andric ReportErrorSummary(description, stack); 27868d75effSDimitry Andric } 27968d75effSDimitry Andric 28068d75effSDimitry Andric void ReportDeadlySignal(const SignalContext &sig, u32 tid, 28168d75effSDimitry Andric UnwindSignalStackCallbackType unwind, 28268d75effSDimitry Andric const void *unwind_context) { 28368d75effSDimitry Andric if (sig.IsStackOverflow()) 28468d75effSDimitry Andric ReportStackOverflowImpl(sig, tid, unwind, unwind_context); 28568d75effSDimitry Andric else 28668d75effSDimitry Andric ReportDeadlySignalImpl(sig, tid, unwind, unwind_context); 28768d75effSDimitry Andric } 28868d75effSDimitry Andric 28968d75effSDimitry Andric void HandleDeadlySignal(void *siginfo, void *context, u32 tid, 29068d75effSDimitry Andric UnwindSignalStackCallbackType unwind, 29168d75effSDimitry Andric const void *unwind_context) { 29268d75effSDimitry Andric StartReportDeadlySignal(); 29368d75effSDimitry Andric ScopedErrorReportLock rl; 29468d75effSDimitry Andric SignalContext sig(siginfo, context); 29568d75effSDimitry Andric ReportDeadlySignal(sig, tid, unwind, unwind_context); 29668d75effSDimitry Andric Report("ABORTING\n"); 29768d75effSDimitry Andric Die(); 29868d75effSDimitry Andric } 29968d75effSDimitry Andric 30068d75effSDimitry Andric #endif // !SANITIZER_FUCHSIA && !SANITIZER_GO 30168d75effSDimitry Andric 302fe6060f1SDimitry Andric atomic_uintptr_t ScopedErrorReportLock::reporting_thread_ = {0}; 303fe6060f1SDimitry Andric StaticSpinMutex ScopedErrorReportLock::mutex_; 30468d75effSDimitry Andric 305fe6060f1SDimitry Andric void ScopedErrorReportLock::Lock() { 30668d75effSDimitry Andric uptr current = GetThreadSelf(); 30768d75effSDimitry Andric for (;;) { 30868d75effSDimitry Andric uptr expected = 0; 309fe6060f1SDimitry Andric if (atomic_compare_exchange_strong(&reporting_thread_, &expected, current, 31068d75effSDimitry Andric memory_order_relaxed)) { 31168d75effSDimitry Andric // We've claimed reporting_thread so proceed. 312fe6060f1SDimitry Andric mutex_.Lock(); 31368d75effSDimitry Andric return; 31468d75effSDimitry Andric } 31568d75effSDimitry Andric 31668d75effSDimitry Andric if (expected == current) { 31768d75effSDimitry Andric // This is either asynch signal or nested error during error reporting. 31868d75effSDimitry Andric // Fail simple to avoid deadlocks in Report(). 31968d75effSDimitry Andric 32068d75effSDimitry Andric // Can't use Report() here because of potential deadlocks in nested 32168d75effSDimitry Andric // signal handlers. 32268d75effSDimitry Andric CatastrophicErrorWrite(SanitizerToolName, 32368d75effSDimitry Andric internal_strlen(SanitizerToolName)); 32468d75effSDimitry Andric static const char msg[] = ": nested bug in the same thread, aborting.\n"; 32568d75effSDimitry Andric CatastrophicErrorWrite(msg, sizeof(msg) - 1); 32668d75effSDimitry Andric 32768d75effSDimitry Andric internal__exit(common_flags()->exitcode); 32868d75effSDimitry Andric } 32968d75effSDimitry Andric 33068d75effSDimitry Andric internal_sched_yield(); 33168d75effSDimitry Andric } 33268d75effSDimitry Andric } 33368d75effSDimitry Andric 334fe6060f1SDimitry Andric void ScopedErrorReportLock::Unlock() { 335fe6060f1SDimitry Andric mutex_.Unlock(); 336fe6060f1SDimitry Andric atomic_store_relaxed(&reporting_thread_, 0); 33768d75effSDimitry Andric } 33868d75effSDimitry Andric 339fe6060f1SDimitry Andric void ScopedErrorReportLock::CheckLocked() { mutex_.CheckLocked(); } 34068d75effSDimitry Andric 34168d75effSDimitry Andric } // namespace __sanitizer 342