1//===-- sanitizer_symbolizer_report.cc ------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9/// 10/// This file is shared between AddressSanitizer and other sanitizer run-time 11/// libraries and implements symbolized reports related functions. 12/// 13//===----------------------------------------------------------------------===// 14 15#include "sanitizer_common.h" 16#include "sanitizer_file.h" 17#include "sanitizer_flags.h" 18#include "sanitizer_procmaps.h" 19#include "sanitizer_report_decorator.h" 20#include "sanitizer_stacktrace.h" 21#include "sanitizer_stacktrace_printer.h" 22#include "sanitizer_symbolizer.h" 23 24#if SANITIZER_POSIX 25# include "sanitizer_posix.h" 26# include <sys/mman.h> 27#endif 28 29namespace __sanitizer { 30 31#if !SANITIZER_GO 32void ReportErrorSummary(const char *error_type, const AddressInfo &info, 33 const char *alt_tool_name) { 34 if (!common_flags()->print_summary) return; 35 InternalScopedString buff(kMaxSummaryLength); 36 buff.append("%s ", error_type); 37 RenderFrame(&buff, "%L %F", 0, info, 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 45bool ReportFile::SupportsColors() { 46 SpinMutexLock l(mu); 47 ReopenIfNecessary(); 48 return SupportsColoredOutput(fd); 49} 50 51static 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. 58static INLINE bool ReportSupportsColors() { return true; } 59 60#endif // !SANITIZER_FUCHSIA 61 62bool 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 73void 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 91void ReportMmapWriteExec(int prot) { 92#if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID) 93 if ((prot & (PROT_WRITE | PROT_EXEC)) != (PROT_WRITE | PROT_EXEC)) 94 return; 95 96 ScopedErrorReportLock l; 97 SanitizerCommonDecorator d; 98 99 InternalMmapVector<BufferedStackTrace> stack_buffer(1); 100 BufferedStackTrace *stack = stack_buffer.data(); 101 stack->Reset(); 102 uptr top = 0; 103 uptr bottom = 0; 104 GET_CALLER_PC_BP_SP; 105 (void)sp; 106 bool fast = common_flags()->fast_unwind_on_fatal; 107 if (fast) 108 GetThreadStackTopAndBottom(false, &top, &bottom); 109 stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, fast); 110 111 Printf("%s", d.Warning()); 112 Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName); 113 Printf("%s", d.Default()); 114 115 stack->Print(); 116 ReportErrorSummary("w-and-x-usage", stack); 117#endif 118} 119 120#if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_GO 121void StartReportDeadlySignal() { 122 // Write the first message using fd=2, just in case. 123 // It may actually fail to write in case stderr is closed. 124 CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName)); 125 static const char kDeadlySignal[] = ":DEADLYSIGNAL\n"; 126 CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1); 127} 128 129static void MaybeReportNonExecRegion(uptr pc) { 130#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD 131 MemoryMappingLayout proc_maps(/*cache_enabled*/ true); 132 MemoryMappedSegment segment; 133 while (proc_maps.Next(&segment)) { 134 if (pc >= segment.start && pc < segment.end && !segment.IsExecutable()) 135 Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n"); 136 } 137#endif 138} 139 140static void PrintMemoryByte(InternalScopedString *str, const char *before, 141 u8 byte) { 142 SanitizerCommonDecorator d; 143 str->append("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15, 144 d.Default()); 145} 146 147static void MaybeDumpInstructionBytes(uptr pc) { 148 if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) 149 return; 150 InternalScopedString str(1024); 151 str.append("First 16 instruction bytes at pc: "); 152 if (IsAccessibleMemoryRange(pc, 16)) { 153 for (int i = 0; i < 16; ++i) { 154 PrintMemoryByte(&str, "", ((u8 *)pc)[i]); 155 } 156 str.append("\n"); 157 } else { 158 str.append("unaccessible\n"); 159 } 160 Report("%s", str.data()); 161} 162 163static void MaybeDumpRegisters(void *context) { 164 if (!common_flags()->dump_registers) return; 165 SignalContext::DumpAllRegisters(context); 166} 167 168static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid, 169 UnwindSignalStackCallbackType unwind, 170 const void *unwind_context) { 171 SanitizerCommonDecorator d; 172 Printf("%s", d.Warning()); 173 static const char kDescription[] = "stack-overflow"; 174 Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n", 175 SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc, 176 (void *)sig.bp, (void *)sig.sp, tid); 177 Printf("%s", d.Default()); 178 InternalMmapVector<BufferedStackTrace> stack_buffer(1); 179 BufferedStackTrace *stack = stack_buffer.data(); 180 stack->Reset(); 181 unwind(sig, unwind_context, stack); 182 stack->Print(); 183 ReportErrorSummary(kDescription, stack); 184} 185 186static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid, 187 UnwindSignalStackCallbackType unwind, 188 const void *unwind_context) { 189 SanitizerCommonDecorator d; 190 Printf("%s", d.Warning()); 191 const char *description = sig.Describe(); 192 Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n", 193 SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc, 194 (void *)sig.bp, (void *)sig.sp, tid); 195 Printf("%s", d.Default()); 196 if (sig.pc < GetPageSizeCached()) 197 Report("Hint: pc points to the zero page.\n"); 198 if (sig.is_memory_access) { 199 const char *access_type = 200 sig.write_flag == SignalContext::WRITE 201 ? "WRITE" 202 : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN"); 203 Report("The signal is caused by a %s memory access.\n", access_type); 204 if (sig.addr < GetPageSizeCached()) 205 Report("Hint: address points to the zero page.\n"); 206 } 207 MaybeReportNonExecRegion(sig.pc); 208 InternalMmapVector<BufferedStackTrace> stack_buffer(1); 209 BufferedStackTrace *stack = stack_buffer.data(); 210 stack->Reset(); 211 unwind(sig, unwind_context, stack); 212 stack->Print(); 213 MaybeDumpInstructionBytes(sig.pc); 214 MaybeDumpRegisters(sig.context); 215 Printf("%s can not provide additional info.\n", SanitizerToolName); 216 ReportErrorSummary(description, stack); 217} 218 219void ReportDeadlySignal(const SignalContext &sig, u32 tid, 220 UnwindSignalStackCallbackType unwind, 221 const void *unwind_context) { 222 if (sig.IsStackOverflow()) 223 ReportStackOverflowImpl(sig, tid, unwind, unwind_context); 224 else 225 ReportDeadlySignalImpl(sig, tid, unwind, unwind_context); 226} 227 228void HandleDeadlySignal(void *siginfo, void *context, u32 tid, 229 UnwindSignalStackCallbackType unwind, 230 const void *unwind_context) { 231 StartReportDeadlySignal(); 232 ScopedErrorReportLock rl; 233 SignalContext sig(siginfo, context); 234 ReportDeadlySignal(sig, tid, unwind, unwind_context); 235 Report("ABORTING\n"); 236 Die(); 237} 238 239#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO 240 241static atomic_uintptr_t reporting_thread = {0}; 242static StaticSpinMutex CommonSanitizerReportMutex; 243 244ScopedErrorReportLock::ScopedErrorReportLock() { 245 uptr current = GetThreadSelf(); 246 for (;;) { 247 uptr expected = 0; 248 if (atomic_compare_exchange_strong(&reporting_thread, &expected, current, 249 memory_order_relaxed)) { 250 // We've claimed reporting_thread so proceed. 251 CommonSanitizerReportMutex.Lock(); 252 return; 253 } 254 255 if (expected == current) { 256 // This is either asynch signal or nested error during error reporting. 257 // Fail simple to avoid deadlocks in Report(). 258 259 // Can't use Report() here because of potential deadlocks in nested 260 // signal handlers. 261 CatastrophicErrorWrite(SanitizerToolName, 262 internal_strlen(SanitizerToolName)); 263 static const char msg[] = ": nested bug in the same thread, aborting.\n"; 264 CatastrophicErrorWrite(msg, sizeof(msg) - 1); 265 266 internal__exit(common_flags()->exitcode); 267 } 268 269 internal_sched_yield(); 270 } 271} 272 273ScopedErrorReportLock::~ScopedErrorReportLock() { 274 CommonSanitizerReportMutex.Unlock(); 275 atomic_store_relaxed(&reporting_thread, 0); 276} 277 278void ScopedErrorReportLock::CheckLocked() { 279 CommonSanitizerReportMutex.CheckLocked(); 280} 281 282} // namespace __sanitizer 283