1 //===-- sanitizer_symbolizer_report.cc ------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 /// This file is shared between AddressSanitizer and other sanitizer run-time
9 /// libraries and implements symbolized reports related functions.
11 //===----------------------------------------------------------------------===//
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"
23 # include "sanitizer_posix.h"
24 # include <sys/mman.h>
27 namespace __sanitizer
{
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
);
41 #if !SANITIZER_FUCHSIA
43 bool ReportFile::SupportsColors() {
46 return SupportsColoredOutput(fd
);
49 static INLINE
bool ReportSupportsColors() {
50 return report_file
.SupportsColors();
53 #else // SANITIZER_FUCHSIA
55 // Fuchsia's logs always go through post-processing that handles colorization.
56 static INLINE
bool ReportSupportsColors() { return true; }
58 #endif // !SANITIZER_FUCHSIA
60 bool ColorizeReports() {
61 // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color
62 // printing on Windows.
63 if (SANITIZER_WINDOWS
)
66 const char *flag
= common_flags()->color
;
67 return internal_strcmp(flag
, "always") == 0 ||
68 (internal_strcmp(flag
, "auto") == 0 && ReportSupportsColors());
71 void ReportErrorSummary(const char *error_type
, const StackTrace
*stack
,
72 const char *alt_tool_name
) {
74 if (!common_flags()->print_summary
)
76 if (stack
->size
== 0) {
77 ReportErrorSummary(error_type
);
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
);
89 void ReportMmapWriteExec(int prot
) {
90 #if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID)
91 if ((prot
& (PROT_WRITE
| PROT_EXEC
)) != (PROT_WRITE
| PROT_EXEC
))
94 ScopedErrorReportLock l
;
95 SanitizerCommonDecorator d
;
97 InternalMmapVector
<BufferedStackTrace
> stack_buffer(1);
98 BufferedStackTrace
*stack
= stack_buffer
.data();
104 bool fast
= common_flags()->fast_unwind_on_fatal
;
106 GetThreadStackTopAndBottom(false, &top
, &bottom
);
107 stack
->Unwind(kStackTraceMax
, pc
, bp
, nullptr, top
, bottom
, fast
);
109 Printf("%s", d
.Warning());
110 Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName
);
111 Printf("%s", d
.Default());
114 ReportErrorSummary("w-and-x-usage", stack
);
118 #if !SANITIZER_FUCHSIA && !SANITIZER_RTEMS && !SANITIZER_GO
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);
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");
138 static void PrintMemoryByte(InternalScopedString
*str
, const char *before
,
140 SanitizerCommonDecorator d
;
141 str
->append("%s%s%x%x%s ", before
, d
.MemoryByte(), byte
>> 4, byte
& 15,
145 static void MaybeDumpInstructionBytes(uptr pc
) {
146 if (!common_flags()->dump_instruction_bytes
|| (pc
< GetPageSizeCached()))
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
]);
156 str
.append("unaccessible\n");
158 Report("%s", str
.data());
161 static void MaybeDumpRegisters(void *context
) {
162 if (!common_flags()->dump_registers
) return;
163 SignalContext::DumpAllRegisters(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();
179 unwind(sig
, unwind_context
, stack
);
181 ReportErrorSummary(kDescription
, stack
);
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
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");
205 MaybeReportNonExecRegion(sig
.pc
);
206 InternalMmapVector
<BufferedStackTrace
> stack_buffer(1);
207 BufferedStackTrace
*stack
= stack_buffer
.data();
209 unwind(sig
, unwind_context
, stack
);
211 MaybeDumpInstructionBytes(sig
.pc
);
212 MaybeDumpRegisters(sig
.context
);
213 Printf("%s can not provide additional info.\n", SanitizerToolName
);
214 ReportErrorSummary(description
, stack
);
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
);
223 ReportDeadlySignalImpl(sig
, tid
, unwind
, 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");
237 #endif // !SANITIZER_FUCHSIA && !SANITIZER_GO
239 static atomic_uintptr_t reporting_thread
= {0};
240 static StaticSpinMutex CommonSanitizerReportMutex
;
242 ScopedErrorReportLock::ScopedErrorReportLock() {
243 uptr current
= GetThreadSelf();
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();
253 if (expected
== current
) {
254 // This is either asynch signal or nested error during error reporting.
255 // Fail simple to avoid deadlocks in Report().
257 // Can't use Report() here because of potential deadlocks in nested
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);
264 internal__exit(common_flags()->exitcode
);
267 internal_sched_yield();
271 ScopedErrorReportLock::~ScopedErrorReportLock() {
272 CommonSanitizerReportMutex
.Unlock();
273 atomic_store_relaxed(&reporting_thread
, 0);
276 void ScopedErrorReportLock::CheckLocked() {
277 CommonSanitizerReportMutex
.CheckLocked();
280 } // namespace __sanitizer