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