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