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