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
27 namespace __sanitizer {
28
29 #if !SANITIZER_GO
ReportErrorSummary(const char * error_type,const AddressInfo & info,const char * alt_tool_name)30 void 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
SupportsColors()43 bool ReportFile::SupportsColors() {
44 SpinMutexLock l(mu);
45 ReopenIfNecessary();
46 return SupportsColoredOutput(fd);
47 }
48
ReportSupportsColors()49 static 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.
ReportSupportsColors()56 static INLINE bool ReportSupportsColors() { return true; }
57
58 #endif // !SANITIZER_FUCHSIA
59
ColorizeReports()60 bool 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
ReportErrorSummary(const char * error_type,const StackTrace * stack,const char * alt_tool_name)71 void 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
ReportMmapWriteExec(int prot)89 void 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
StartReportDeadlySignal()119 void 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
MaybeReportNonExecRegion(uptr pc)127 static 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
PrintMemoryByte(InternalScopedString * str,const char * before,u8 byte)138 static 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
MaybeDumpInstructionBytes(uptr pc)145 static 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
MaybeDumpRegisters(void * context)161 static void MaybeDumpRegisters(void *context) {
162 if (!common_flags()->dump_registers) return;
163 SignalContext::DumpAllRegisters(context);
164 }
165
ReportStackOverflowImpl(const SignalContext & sig,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)166 static 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
ReportDeadlySignalImpl(const SignalContext & sig,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)184 static 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
ReportDeadlySignal(const SignalContext & sig,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)217 void 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
HandleDeadlySignal(void * siginfo,void * context,u32 tid,UnwindSignalStackCallbackType unwind,const void * unwind_context)226 void 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
239 static atomic_uintptr_t reporting_thread = {0};
240 static StaticSpinMutex CommonSanitizerReportMutex;
241
ScopedErrorReportLock()242 ScopedErrorReportLock::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
~ScopedErrorReportLock()271 ScopedErrorReportLock::~ScopedErrorReportLock() {
272 CommonSanitizerReportMutex.Unlock();
273 atomic_store_relaxed(&reporting_thread, 0);
274 }
275
CheckLocked()276 void ScopedErrorReportLock::CheckLocked() {
277 CommonSanitizerReportMutex.CheckLocked();
278 }
279
280 } // namespace __sanitizer
281