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