168d75effSDimitry Andric //===-- sanitizer_symbolizer_report.cpp -----------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric ///
968d75effSDimitry Andric /// This file is shared between AddressSanitizer and other sanitizer run-time
1068d75effSDimitry Andric /// libraries and implements symbolized reports related functions.
1168d75effSDimitry Andric ///
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
1468d75effSDimitry Andric #include "sanitizer_common.h"
1568d75effSDimitry Andric #include "sanitizer_file.h"
1668d75effSDimitry Andric #include "sanitizer_flags.h"
1768d75effSDimitry Andric #include "sanitizer_procmaps.h"
1868d75effSDimitry Andric #include "sanitizer_report_decorator.h"
1968d75effSDimitry Andric #include "sanitizer_stacktrace.h"
2068d75effSDimitry Andric #include "sanitizer_stacktrace_printer.h"
2168d75effSDimitry Andric #include "sanitizer_symbolizer.h"
2268d75effSDimitry Andric 
2368d75effSDimitry Andric #if SANITIZER_POSIX
2468d75effSDimitry Andric # include "sanitizer_posix.h"
2568d75effSDimitry Andric # include <sys/mman.h>
2668d75effSDimitry Andric #endif
2768d75effSDimitry Andric 
2868d75effSDimitry Andric namespace __sanitizer {
2968d75effSDimitry Andric 
3068d75effSDimitry Andric #if !SANITIZER_GO
311db9f3b2SDimitry Andric 
321db9f3b2SDimitry Andric static bool FrameIsInternal(const SymbolizedStack *frame) {
331db9f3b2SDimitry Andric   if (!frame)
341db9f3b2SDimitry Andric     return true;
351db9f3b2SDimitry Andric   const char *file = frame->info.file;
361db9f3b2SDimitry Andric   const char *module = frame->info.module;
37b3edf446SDimitry Andric   // On Gentoo, the path is g++-*, so there's *not* a missing /.
387a6dacacSDimitry Andric   if (file && (internal_strstr(file, "/compiler-rt/lib/") ||
39b3edf446SDimitry Andric                internal_strstr(file, "/include/c++/") ||
40b3edf446SDimitry Andric                internal_strstr(file, "/include/g++")))
411db9f3b2SDimitry Andric     return true;
42*0fca6ea1SDimitry Andric   if (file && internal_strstr(file, "\\compiler-rt\\lib\\"))
43*0fca6ea1SDimitry Andric     return true;
441db9f3b2SDimitry Andric   if (module && (internal_strstr(module, "libclang_rt.")))
451db9f3b2SDimitry Andric     return true;
46*0fca6ea1SDimitry Andric   if (module && (internal_strstr(module, "clang_rt.")))
47*0fca6ea1SDimitry Andric     return true;
481db9f3b2SDimitry Andric   return false;
491db9f3b2SDimitry Andric }
501db9f3b2SDimitry Andric 
511db9f3b2SDimitry Andric const SymbolizedStack *SkipInternalFrames(const SymbolizedStack *frames) {
521db9f3b2SDimitry Andric   for (const SymbolizedStack *f = frames; f; f = f->next)
531db9f3b2SDimitry Andric     if (!FrameIsInternal(f))
541db9f3b2SDimitry Andric       return f;
551db9f3b2SDimitry Andric   return nullptr;
561db9f3b2SDimitry Andric }
571db9f3b2SDimitry Andric 
5868d75effSDimitry Andric void ReportErrorSummary(const char *error_type, const AddressInfo &info,
5968d75effSDimitry Andric                         const char *alt_tool_name) {
6068d75effSDimitry Andric   if (!common_flags()->print_summary) return;
61fe6060f1SDimitry Andric   InternalScopedString buff;
625f757f3fSDimitry Andric   buff.AppendF("%s ", error_type);
635f757f3fSDimitry Andric   StackTracePrinter::GetOrInit()->RenderFrame(
645f757f3fSDimitry Andric       &buff, "%L %F", 0, info.address, &info,
655f757f3fSDimitry Andric       common_flags()->symbolize_vs_style, common_flags()->strip_path_prefix);
6668d75effSDimitry Andric   ReportErrorSummary(buff.data(), alt_tool_name);
6768d75effSDimitry Andric }
6868d75effSDimitry Andric #endif
6968d75effSDimitry Andric 
7068d75effSDimitry Andric #if !SANITIZER_FUCHSIA
7168d75effSDimitry Andric 
7268d75effSDimitry Andric bool ReportFile::SupportsColors() {
7368d75effSDimitry Andric   SpinMutexLock l(mu);
7468d75effSDimitry Andric   ReopenIfNecessary();
7568d75effSDimitry Andric   return SupportsColoredOutput(fd);
7668d75effSDimitry Andric }
7768d75effSDimitry Andric 
78e8d8bef9SDimitry Andric static inline bool ReportSupportsColors() {
7968d75effSDimitry Andric   return report_file.SupportsColors();
8068d75effSDimitry Andric }
8168d75effSDimitry Andric 
8268d75effSDimitry Andric #else  // SANITIZER_FUCHSIA
8368d75effSDimitry Andric 
8468d75effSDimitry Andric // Fuchsia's logs always go through post-processing that handles colorization.
85e8d8bef9SDimitry Andric static inline bool ReportSupportsColors() { return true; }
8668d75effSDimitry Andric 
8768d75effSDimitry Andric #endif  // !SANITIZER_FUCHSIA
8868d75effSDimitry Andric 
8968d75effSDimitry Andric bool ColorizeReports() {
9068d75effSDimitry Andric   // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
9168d75effSDimitry Andric   // printing on Windows.
9268d75effSDimitry Andric   if (SANITIZER_WINDOWS)
9368d75effSDimitry Andric     return false;
9468d75effSDimitry Andric 
9568d75effSDimitry Andric   const char *flag = common_flags()->color;
9668d75effSDimitry Andric   return internal_strcmp(flag, "always") == 0 ||
9768d75effSDimitry Andric          (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors());
9868d75effSDimitry Andric }
9968d75effSDimitry Andric 
10068d75effSDimitry Andric void ReportErrorSummary(const char *error_type, const StackTrace *stack,
10168d75effSDimitry Andric                         const char *alt_tool_name) {
10268d75effSDimitry Andric #if !SANITIZER_GO
10368d75effSDimitry Andric   if (!common_flags()->print_summary)
10468d75effSDimitry Andric     return;
105297eecfbSDimitry Andric 
106297eecfbSDimitry Andric   // Find first non-internal stack frame.
107297eecfbSDimitry Andric   for (uptr i = 0; i < stack->size; ++i) {
108297eecfbSDimitry Andric     uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[i]);
109297eecfbSDimitry Andric     SymbolizedStackHolder symbolized_stack(
110297eecfbSDimitry Andric         Symbolizer::GetOrInit()->SymbolizePC(pc));
111297eecfbSDimitry Andric     if (const SymbolizedStack *frame = symbolized_stack.get()) {
112297eecfbSDimitry Andric       if (const SymbolizedStack *summary_frame = SkipInternalFrames(frame)) {
113297eecfbSDimitry Andric         ReportErrorSummary(error_type, summary_frame->info, alt_tool_name);
11468d75effSDimitry Andric         return;
11568d75effSDimitry Andric       }
116297eecfbSDimitry Andric     }
117297eecfbSDimitry Andric   }
118297eecfbSDimitry Andric 
119297eecfbSDimitry Andric   // Fallback to the top one.
120297eecfbSDimitry Andric   if (stack->size) {
12168d75effSDimitry Andric     uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]);
1221db9f3b2SDimitry Andric     SymbolizedStackHolder symbolized_stack(
1231db9f3b2SDimitry Andric         Symbolizer::GetOrInit()->SymbolizePC(pc));
124297eecfbSDimitry Andric     if (const SymbolizedStack *frame = symbolized_stack.get()) {
12568d75effSDimitry Andric       ReportErrorSummary(error_type, frame->info, alt_tool_name);
126297eecfbSDimitry Andric       return;
127297eecfbSDimitry Andric     }
128297eecfbSDimitry Andric   }
129297eecfbSDimitry Andric 
130297eecfbSDimitry Andric   // Fallback to a summary without location.
131297eecfbSDimitry Andric   ReportErrorSummary(error_type);
13268d75effSDimitry Andric #endif
13368d75effSDimitry Andric }
13468d75effSDimitry Andric 
135349cc55cSDimitry Andric void ReportMmapWriteExec(int prot, int flags) {
13668d75effSDimitry Andric #if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID)
137349cc55cSDimitry Andric   int pflags = (PROT_WRITE | PROT_EXEC);
138349cc55cSDimitry Andric   if ((prot & pflags) != pflags)
13968d75effSDimitry Andric     return;
14068d75effSDimitry Andric 
14181ad6265SDimitry Andric #  if SANITIZER_APPLE && defined(MAP_JIT)
142349cc55cSDimitry Andric   if ((flags & MAP_JIT) == MAP_JIT)
143349cc55cSDimitry Andric     return;
144349cc55cSDimitry Andric #  endif
145349cc55cSDimitry Andric 
14668d75effSDimitry Andric   ScopedErrorReportLock l;
14768d75effSDimitry Andric   SanitizerCommonDecorator d;
14868d75effSDimitry Andric 
14968d75effSDimitry Andric   InternalMmapVector<BufferedStackTrace> stack_buffer(1);
15068d75effSDimitry Andric   BufferedStackTrace *stack = stack_buffer.data();
15168d75effSDimitry Andric   stack->Reset();
15268d75effSDimitry Andric   uptr top = 0;
15368d75effSDimitry Andric   uptr bottom = 0;
15406c3fb27SDimitry Andric   GET_CALLER_PC_BP;
15568d75effSDimitry Andric   bool fast = common_flags()->fast_unwind_on_fatal;
15668d75effSDimitry Andric   if (StackTrace::WillUseFastUnwind(fast)) {
15768d75effSDimitry Andric     GetThreadStackTopAndBottom(false, &top, &bottom);
15868d75effSDimitry Andric     stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, true);
15968d75effSDimitry Andric   } else {
16068d75effSDimitry Andric     stack->Unwind(kStackTraceMax, pc, 0, nullptr, 0, 0, false);
16168d75effSDimitry Andric   }
16268d75effSDimitry Andric 
16368d75effSDimitry Andric   Printf("%s", d.Warning());
16468d75effSDimitry Andric   Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName);
16568d75effSDimitry Andric   Printf("%s", d.Default());
16668d75effSDimitry Andric 
16768d75effSDimitry Andric   stack->Print();
16868d75effSDimitry Andric   ReportErrorSummary("w-and-x-usage", stack);
16968d75effSDimitry Andric #endif
17068d75effSDimitry Andric }
17168d75effSDimitry Andric 
172fe6060f1SDimitry Andric #if !SANITIZER_FUCHSIA && !SANITIZER_GO
17368d75effSDimitry Andric void StartReportDeadlySignal() {
17468d75effSDimitry Andric   // Write the first message using fd=2, just in case.
17568d75effSDimitry Andric   // It may actually fail to write in case stderr is closed.
17668d75effSDimitry Andric   CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName));
17768d75effSDimitry Andric   static const char kDeadlySignal[] = ":DEADLYSIGNAL\n";
17868d75effSDimitry Andric   CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1);
17968d75effSDimitry Andric }
18068d75effSDimitry Andric 
18168d75effSDimitry Andric static void MaybeReportNonExecRegion(uptr pc) {
18268d75effSDimitry Andric #if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD
18368d75effSDimitry Andric   MemoryMappingLayout proc_maps(/*cache_enabled*/ true);
18468d75effSDimitry Andric   MemoryMappedSegment segment;
18568d75effSDimitry Andric   while (proc_maps.Next(&segment)) {
18668d75effSDimitry Andric     if (pc >= segment.start && pc < segment.end && !segment.IsExecutable())
18768d75effSDimitry Andric       Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n");
18868d75effSDimitry Andric   }
18968d75effSDimitry Andric #endif
19068d75effSDimitry Andric }
19168d75effSDimitry Andric 
19268d75effSDimitry Andric static void PrintMemoryByte(InternalScopedString *str, const char *before,
19368d75effSDimitry Andric                             u8 byte) {
19468d75effSDimitry Andric   SanitizerCommonDecorator d;
1955f757f3fSDimitry Andric   str->AppendF("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15,
19668d75effSDimitry Andric                d.Default());
19768d75effSDimitry Andric }
19868d75effSDimitry Andric 
19968d75effSDimitry Andric static void MaybeDumpInstructionBytes(uptr pc) {
20068d75effSDimitry Andric   if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached()))
20168d75effSDimitry Andric     return;
202fe6060f1SDimitry Andric   InternalScopedString str;
2035f757f3fSDimitry Andric   str.AppendF("First 16 instruction bytes at pc: ");
20468d75effSDimitry Andric   if (IsAccessibleMemoryRange(pc, 16)) {
20568d75effSDimitry Andric     for (int i = 0; i < 16; ++i) {
20668d75effSDimitry Andric       PrintMemoryByte(&str, "", ((u8 *)pc)[i]);
20768d75effSDimitry Andric     }
2085f757f3fSDimitry Andric     str.AppendF("\n");
20968d75effSDimitry Andric   } else {
2105f757f3fSDimitry Andric     str.AppendF("unaccessible\n");
21168d75effSDimitry Andric   }
21268d75effSDimitry Andric   Report("%s", str.data());
21368d75effSDimitry Andric }
21468d75effSDimitry Andric 
21568d75effSDimitry Andric static void MaybeDumpRegisters(void *context) {
21668d75effSDimitry Andric   if (!common_flags()->dump_registers) return;
21768d75effSDimitry Andric   SignalContext::DumpAllRegisters(context);
21868d75effSDimitry Andric }
21968d75effSDimitry Andric 
22068d75effSDimitry Andric static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid,
22168d75effSDimitry Andric                                     UnwindSignalStackCallbackType unwind,
22268d75effSDimitry Andric                                     const void *unwind_context) {
22368d75effSDimitry Andric   SanitizerCommonDecorator d;
22468d75effSDimitry Andric   Printf("%s", d.Warning());
22568d75effSDimitry Andric   static const char kDescription[] = "stack-overflow";
22668d75effSDimitry Andric   Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n",
22768d75effSDimitry Andric          SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc,
22868d75effSDimitry Andric          (void *)sig.bp, (void *)sig.sp, tid);
22968d75effSDimitry Andric   Printf("%s", d.Default());
23068d75effSDimitry Andric   InternalMmapVector<BufferedStackTrace> stack_buffer(1);
23168d75effSDimitry Andric   BufferedStackTrace *stack = stack_buffer.data();
23268d75effSDimitry Andric   stack->Reset();
23368d75effSDimitry Andric   unwind(sig, unwind_context, stack);
23468d75effSDimitry Andric   stack->Print();
23568d75effSDimitry Andric   ReportErrorSummary(kDescription, stack);
23668d75effSDimitry Andric }
23768d75effSDimitry Andric 
23868d75effSDimitry Andric static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid,
23968d75effSDimitry Andric                                    UnwindSignalStackCallbackType unwind,
24068d75effSDimitry Andric                                    const void *unwind_context) {
24168d75effSDimitry Andric   SanitizerCommonDecorator d;
24268d75effSDimitry Andric   Printf("%s", d.Warning());
24368d75effSDimitry Andric   const char *description = sig.Describe();
24468d75effSDimitry Andric   if (sig.is_memory_access && !sig.is_true_faulting_addr)
24568d75effSDimitry Andric     Report("ERROR: %s: %s on unknown address (pc %p bp %p sp %p T%d)\n",
24668d75effSDimitry Andric            SanitizerToolName, description, (void *)sig.pc, (void *)sig.bp,
24768d75effSDimitry Andric            (void *)sig.sp, tid);
24868d75effSDimitry Andric   else
24968d75effSDimitry Andric     Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n",
25068d75effSDimitry Andric            SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc,
25168d75effSDimitry Andric            (void *)sig.bp, (void *)sig.sp, tid);
25268d75effSDimitry Andric   Printf("%s", d.Default());
25368d75effSDimitry Andric   if (sig.pc < GetPageSizeCached())
25468d75effSDimitry Andric     Report("Hint: pc points to the zero page.\n");
25568d75effSDimitry Andric   if (sig.is_memory_access) {
25668d75effSDimitry Andric     const char *access_type =
257d56accc7SDimitry Andric         sig.write_flag == SignalContext::Write
25868d75effSDimitry Andric             ? "WRITE"
259d56accc7SDimitry Andric             : (sig.write_flag == SignalContext::Read ? "READ" : "UNKNOWN");
26068d75effSDimitry Andric     Report("The signal is caused by a %s memory access.\n", access_type);
26168d75effSDimitry Andric     if (!sig.is_true_faulting_addr)
26268d75effSDimitry Andric       Report("Hint: this fault was caused by a dereference of a high value "
263e8d8bef9SDimitry Andric              "address (see register values below).  Disassemble the provided "
26468d75effSDimitry Andric              "pc to learn which register was used.\n");
26568d75effSDimitry Andric     else if (sig.addr < GetPageSizeCached())
26668d75effSDimitry Andric       Report("Hint: address points to the zero page.\n");
26768d75effSDimitry Andric   }
26868d75effSDimitry Andric   MaybeReportNonExecRegion(sig.pc);
26968d75effSDimitry Andric   InternalMmapVector<BufferedStackTrace> stack_buffer(1);
27068d75effSDimitry Andric   BufferedStackTrace *stack = stack_buffer.data();
27168d75effSDimitry Andric   stack->Reset();
27268d75effSDimitry Andric   unwind(sig, unwind_context, stack);
27368d75effSDimitry Andric   stack->Print();
27468d75effSDimitry Andric   MaybeDumpInstructionBytes(sig.pc);
27568d75effSDimitry Andric   MaybeDumpRegisters(sig.context);
27668d75effSDimitry Andric   Printf("%s can not provide additional info.\n", SanitizerToolName);
27768d75effSDimitry Andric   ReportErrorSummary(description, stack);
27868d75effSDimitry Andric }
27968d75effSDimitry Andric 
28068d75effSDimitry Andric void ReportDeadlySignal(const SignalContext &sig, u32 tid,
28168d75effSDimitry Andric                         UnwindSignalStackCallbackType unwind,
28268d75effSDimitry Andric                         const void *unwind_context) {
28368d75effSDimitry Andric   if (sig.IsStackOverflow())
28468d75effSDimitry Andric     ReportStackOverflowImpl(sig, tid, unwind, unwind_context);
28568d75effSDimitry Andric   else
28668d75effSDimitry Andric     ReportDeadlySignalImpl(sig, tid, unwind, unwind_context);
28768d75effSDimitry Andric }
28868d75effSDimitry Andric 
28968d75effSDimitry Andric void HandleDeadlySignal(void *siginfo, void *context, u32 tid,
29068d75effSDimitry Andric                         UnwindSignalStackCallbackType unwind,
29168d75effSDimitry Andric                         const void *unwind_context) {
29268d75effSDimitry Andric   StartReportDeadlySignal();
29368d75effSDimitry Andric   ScopedErrorReportLock rl;
29468d75effSDimitry Andric   SignalContext sig(siginfo, context);
29568d75effSDimitry Andric   ReportDeadlySignal(sig, tid, unwind, unwind_context);
29668d75effSDimitry Andric   Report("ABORTING\n");
29768d75effSDimitry Andric   Die();
29868d75effSDimitry Andric }
29968d75effSDimitry Andric 
30068d75effSDimitry Andric #endif  // !SANITIZER_FUCHSIA && !SANITIZER_GO
30168d75effSDimitry Andric 
302fe6060f1SDimitry Andric atomic_uintptr_t ScopedErrorReportLock::reporting_thread_ = {0};
303fe6060f1SDimitry Andric StaticSpinMutex ScopedErrorReportLock::mutex_;
30468d75effSDimitry Andric 
305fe6060f1SDimitry Andric void ScopedErrorReportLock::Lock() {
30668d75effSDimitry Andric   uptr current = GetThreadSelf();
30768d75effSDimitry Andric   for (;;) {
30868d75effSDimitry Andric     uptr expected = 0;
309fe6060f1SDimitry Andric     if (atomic_compare_exchange_strong(&reporting_thread_, &expected, current,
31068d75effSDimitry Andric                                        memory_order_relaxed)) {
31168d75effSDimitry Andric       // We've claimed reporting_thread so proceed.
312fe6060f1SDimitry Andric       mutex_.Lock();
31368d75effSDimitry Andric       return;
31468d75effSDimitry Andric     }
31568d75effSDimitry Andric 
31668d75effSDimitry Andric     if (expected == current) {
31768d75effSDimitry Andric       // This is either asynch signal or nested error during error reporting.
31868d75effSDimitry Andric       // Fail simple to avoid deadlocks in Report().
31968d75effSDimitry Andric 
32068d75effSDimitry Andric       // Can't use Report() here because of potential deadlocks in nested
32168d75effSDimitry Andric       // signal handlers.
32268d75effSDimitry Andric       CatastrophicErrorWrite(SanitizerToolName,
32368d75effSDimitry Andric                              internal_strlen(SanitizerToolName));
32468d75effSDimitry Andric       static const char msg[] = ": nested bug in the same thread, aborting.\n";
32568d75effSDimitry Andric       CatastrophicErrorWrite(msg, sizeof(msg) - 1);
32668d75effSDimitry Andric 
32768d75effSDimitry Andric       internal__exit(common_flags()->exitcode);
32868d75effSDimitry Andric     }
32968d75effSDimitry Andric 
33068d75effSDimitry Andric     internal_sched_yield();
33168d75effSDimitry Andric   }
33268d75effSDimitry Andric }
33368d75effSDimitry Andric 
334fe6060f1SDimitry Andric void ScopedErrorReportLock::Unlock() {
335fe6060f1SDimitry Andric   mutex_.Unlock();
336fe6060f1SDimitry Andric   atomic_store_relaxed(&reporting_thread_, 0);
33768d75effSDimitry Andric }
33868d75effSDimitry Andric 
339fe6060f1SDimitry Andric void ScopedErrorReportLock::CheckLocked() { mutex_.CheckLocked(); }
34068d75effSDimitry Andric 
34168d75effSDimitry Andric }  // namespace __sanitizer
342