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 28 namespace __sanitizer { 29 30 #if !SANITIZER_GO 31 32 static 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 51 const 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 58 void 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 #endif 69 70 #if !SANITIZER_FUCHSIA 71 72 bool ReportFile::SupportsColors() { 73 SpinMutexLock l(mu); 74 ReopenIfNecessary(); 75 return SupportsColoredOutput(fd); 76 } 77 78 static inline bool ReportSupportsColors() { 79 return report_file.SupportsColors(); 80 } 81 82 #else // SANITIZER_FUCHSIA 83 84 // Fuchsia's logs always go through post-processing that handles colorization. 85 static inline bool ReportSupportsColors() { return true; } 86 87 #endif // !SANITIZER_FUCHSIA 88 89 bool ColorizeReports() { 90 // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color 91 // 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 100 void ReportErrorSummary(const char *error_type, const StackTrace *stack, 101 const char *alt_tool_name) { 102 #if !SANITIZER_GO 103 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 #endif 133 } 134 135 void 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 # endif 145 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 #endif 170 } 171 172 #if !SANITIZER_FUCHSIA && !SANITIZER_GO 173 void 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 181 static void MaybeReportNonExecRegion(uptr pc) { 182 #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD 183 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 #endif 190 } 191 192 static 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 199 static 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 215 static void MaybeDumpRegisters(void *context) { 216 if (!common_flags()->dump_registers) return; 217 SignalContext::DumpAllRegisters(context); 218 } 219 220 static 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 241 static 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 else 252 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::Write 261 ? "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 283 void 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 else 289 ReportDeadlySignalImpl(sig, tid, unwind, unwind_context); 290 } 291 292 void 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_GO 304 305 atomic_uintptr_t ScopedErrorReportLock::reporting_thread_ = {0}; 306 StaticSpinMutex ScopedErrorReportLock::mutex_; 307 308 void 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 nested 324 // 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 337 void ScopedErrorReportLock::Unlock() { 338 mutex_.Unlock(); 339 atomic_store_relaxed(&reporting_thread_, 0); 340 } 341 342 void ScopedErrorReportLock::CheckLocked() { mutex_.CheckLocked(); } 343 344 } // namespace __sanitizer 345