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