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