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