xref: /netbsd-src/external/gpl3/gcc.old/dist/libsanitizer/sanitizer_common/sanitizer_symbolizer_report.cc (revision 627f7eb200a4419d89b531d55fccd2ee3ffdcde0)
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 
27 namespace __sanitizer {
28 
29 #if !SANITIZER_GO
ReportErrorSummary(const char * error_type,const AddressInfo & info,const char * alt_tool_name)30 void 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 
SupportsColors()43 bool ReportFile::SupportsColors() {
44   SpinMutexLock l(mu);
45   ReopenIfNecessary();
46   return SupportsColoredOutput(fd);
47 }
48 
ReportSupportsColors()49 static 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.
ReportSupportsColors()56 static INLINE bool ReportSupportsColors() { return true; }
57 
58 #endif  // !SANITIZER_FUCHSIA
59 
ColorizeReports()60 bool 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 
ReportErrorSummary(const char * error_type,const StackTrace * stack,const char * alt_tool_name)71 void 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 
ReportMmapWriteExec(int prot)89 void 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
StartReportDeadlySignal()119 void 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 
MaybeReportNonExecRegion(uptr pc)127 static 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 
PrintMemoryByte(InternalScopedString * str,const char * before,u8 byte)138 static 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 
MaybeDumpInstructionBytes(uptr pc)145 static 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 
MaybeDumpRegisters(void * context)161 static void MaybeDumpRegisters(void *context) {
162   if (!common_flags()->dump_registers) return;
163   SignalContext::DumpAllRegisters(context);
164 }
165 
ReportStackOverflowImpl(const SignalContext & sig,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)166 static 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 
ReportDeadlySignalImpl(const SignalContext & sig,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)184 static 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 
ReportDeadlySignal(const SignalContext & sig,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)217 void 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 
HandleDeadlySignal(void * siginfo,void * context,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)226 void 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 
239 static atomic_uintptr_t reporting_thread = {0};
240 static StaticSpinMutex CommonSanitizerReportMutex;
241 
ScopedErrorReportLock()242 ScopedErrorReportLock::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 
~ScopedErrorReportLock()271 ScopedErrorReportLock::~ScopedErrorReportLock() {
272   CommonSanitizerReportMutex.Unlock();
273   atomic_store_relaxed(&reporting_thread, 0);
274 }
275 
CheckLocked()276 void ScopedErrorReportLock::CheckLocked() {
277   CommonSanitizerReportMutex.CheckLocked();
278 }
279 
280 }  // namespace __sanitizer
281