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