Index: lib/asan/CMakeLists.txt =================================================================== --- lib/asan/CMakeLists.txt +++ lib/asan/CMakeLists.txt @@ -126,6 +126,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTLSanCommon RTUbsan CFLAGS ${ASAN_DYNAMIC_CFLAGS} @@ -140,6 +141,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTLSanCommon RTUbsan) Index: lib/asan/tests/CMakeLists.txt =================================================================== --- lib/asan/tests/CMakeLists.txt +++ lib/asan/tests/CMakeLists.txt @@ -252,6 +252,7 @@ $ $ $ + $ $ $) else() @@ -262,6 +263,7 @@ $ $ $ + $ $ $ $) @@ -286,6 +288,7 @@ $ $ $ + $ $ $ ${COMPILER_RT_GTEST_SOURCE} Index: lib/esan/CMakeLists.txt =================================================================== --- lib/esan/CMakeLists.txt +++ lib/esan/CMakeLists.txt @@ -26,6 +26,7 @@ $ $ $ + $ CFLAGS ${ESAN_RTL_CFLAGS}) add_sanitizer_rt_symbols(clang_rt.esan ARCHS ${arch} Index: lib/hwasan/CMakeLists.txt =================================================================== --- lib/hwasan/CMakeLists.txt +++ lib/hwasan/CMakeLists.txt @@ -79,6 +79,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTUbsan CFLAGS ${HWASAN_RTL_CFLAGS} PARENT_TARGET hwasan) @@ -114,6 +115,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTUbsan # The only purpose of RTHWAsan_dynamic_version_script_dummy is to # carry a dependency of the shared runtime on the version script. Index: lib/lsan/CMakeLists.txt =================================================================== --- lib/lsan/CMakeLists.txt +++ lib/lsan/CMakeLists.txt @@ -44,6 +44,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer CFLAGS ${LSAN_CFLAGS} LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS} LINK_LIBS ${LSAN_LINK_LIBS} @@ -58,6 +59,7 @@ $ $ $ + $ $ CFLAGS ${LSAN_CFLAGS} PARENT_TARGET lsan) Index: lib/msan/CMakeLists.txt =================================================================== --- lib/msan/CMakeLists.txt +++ lib/msan/CMakeLists.txt @@ -41,6 +41,7 @@ $ $ $ + $ $ CFLAGS ${MSAN_RTL_CFLAGS} PARENT_TARGET msan) Index: lib/sanitizer_common/CMakeLists.txt =================================================================== --- lib/sanitizer_common/CMakeLists.txt +++ lib/sanitizer_common/CMakeLists.txt @@ -31,16 +31,8 @@ sanitizer_procmaps_mac.cc sanitizer_procmaps_solaris.cc sanitizer_solaris.cc - sanitizer_stackdepot.cc - sanitizer_stacktrace.cc - sanitizer_stacktrace_printer.cc sanitizer_stoptheworld_mac.cc sanitizer_suppressions.cc - sanitizer_symbolizer.cc - sanitizer_symbolizer_fuchsia.cc - sanitizer_symbolizer_libbacktrace.cc - sanitizer_symbolizer_mac.cc - sanitizer_symbolizer_win.cc sanitizer_tls_get_addr.cc sanitizer_thread_registry.cc sanitizer_win.cc) @@ -67,12 +59,8 @@ sanitizer_linux_libcdep.cc sanitizer_mac_libcdep.cc sanitizer_posix_libcdep.cc - sanitizer_stacktrace_libcdep.cc sanitizer_stoptheworld_linux_libcdep.cc - sanitizer_symbolizer_libcdep.cc - sanitizer_symbolizer_posix_libcdep.cc - sanitizer_unwind_linux_libcdep.cc - sanitizer_unwind_win.cc) +) set(SANITIZER_COVERAGE_SOURCES sancov_flags.cc @@ -80,6 +68,22 @@ sanitizer_coverage_libcdep_new.cc sanitizer_coverage_win_sections.cc) +set(SANITIZER_SYMBOLIZER_SOURCES + sanitizer_stackdepot.cc + sanitizer_stacktrace.cc + sanitizer_stacktrace_libcdep.cc + sanitizer_stacktrace_printer.cc + sanitizer_symbolizer.cc + sanitizer_symbolizer_fuchsia.cc + sanitizer_symbolizer_libbacktrace.cc + sanitizer_symbolizer_libcdep.cc + sanitizer_symbolizer_mac.cc + sanitizer_symbolizer_posix_libcdep.cc + sanitizer_symbolizer_report.cc + sanitizer_symbolizer_win.cc + sanitizer_unwind_linux_libcdep.cc + sanitizer_unwind_win.cc) + # Explicitly list all sanitizer_common headers. Not all of these are # included in sanitizer_common source files, but we need to depend on # headers when building our custom unit tests. @@ -217,6 +221,12 @@ SOURCES ${SANITIZER_COVERAGE_SOURCES} CFLAGS ${SANITIZER_CFLAGS} DEFS ${SANITIZER_COMMON_DEFINITIONS}) +add_compiler_rt_object_libraries(RTSanitizerCommonSymbolizer + ${OS_OPTION} + ARCHS ${SANITIZER_COMMON_SUPPORTED_ARCH} + SOURCES ${SANITIZER_SYMBOLIZER_SOURCES} + CFLAGS ${SANITIZER_CFLAGS} + DEFS ${SANITIZER_COMMON_DEFINITIONS}) set(SANITIZER_NO_WEAK_HOOKS_CFLAGS ${SANITIZER_CFLAGS}) list(APPEND SANITIZER_NO_WEAK_HOOKS_CFLAGS "-DSANITIZER_SUPPORTS_WEAK_HOOKS=0") Index: lib/sanitizer_common/sanitizer_common.cc =================================================================== --- lib/sanitizer_common/sanitizer_common.cc +++ lib/sanitizer_common/sanitizer_common.cc @@ -17,8 +17,6 @@ #include "sanitizer_flags.h" #include "sanitizer_libc.h" #include "sanitizer_placement_new.h" -#include "sanitizer_stacktrace_printer.h" -#include "sanitizer_symbolizer.h" namespace __sanitizer { @@ -107,18 +105,6 @@ __sanitizer_report_error_summary(buff.data()); } -#if !SANITIZER_GO -void ReportErrorSummary(const char *error_type, const AddressInfo &info, - const char *alt_tool_name) { - if (!common_flags()->print_summary) return; - InternalScopedString buff(kMaxSummaryLength); - buff.append("%s ", error_type); - RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style, - common_flags()->strip_path_prefix); - ReportErrorSummary(buff.data(), alt_tool_name); -} -#endif - // Removes the ANSI escape sequences from the input string (in-place). void RemoveANSIEscapeSequencesFromString(char *str) { if (!str) Index: lib/sanitizer_common/sanitizer_common_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_common_libcdep.cc +++ lib/sanitizer_common/sanitizer_common_libcdep.cc @@ -11,99 +11,13 @@ // run-time libraries. //===----------------------------------------------------------------------===// -#include "sanitizer_common.h" - #include "sanitizer_allocator_interface.h" -#include "sanitizer_file.h" +#include "sanitizer_common.h" #include "sanitizer_flags.h" #include "sanitizer_procmaps.h" -#include "sanitizer_report_decorator.h" -#include "sanitizer_stacktrace.h" -#include "sanitizer_symbolizer.h" - -#if SANITIZER_POSIX -#include "sanitizer_posix.h" -#include -#endif - -namespace __sanitizer { - -#if !SANITIZER_FUCHSIA - -bool ReportFile::SupportsColors() { - SpinMutexLock l(mu); - ReopenIfNecessary(); - return SupportsColoredOutput(fd); -} - -static INLINE bool ReportSupportsColors() { - return report_file.SupportsColors(); -} - -#else // SANITIZER_FUCHSIA - -// Fuchsia's logs always go through post-processing that handles colorization. -static INLINE bool ReportSupportsColors() { return true; } - -#endif // !SANITIZER_FUCHSIA - -bool ColorizeReports() { - // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color - // printing on Windows. - if (SANITIZER_WINDOWS) - return false; - - const char *flag = common_flags()->color; - return internal_strcmp(flag, "always") == 0 || - (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors()); -} - -void ReportErrorSummary(const char *error_type, const StackTrace *stack, - const char *alt_tool_name) { -#if !SANITIZER_GO - if (!common_flags()->print_summary) - return; - if (stack->size == 0) { - ReportErrorSummary(error_type); - return; - } - // Currently, we include the first stack frame into the report summary. - // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). - uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); - SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); - ReportErrorSummary(error_type, frame->info, alt_tool_name); - frame->ClearAll(); -#endif -} -void ReportMmapWriteExec(int prot) { -#if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID) - if ((prot & (PROT_WRITE | PROT_EXEC)) != (PROT_WRITE | PROT_EXEC)) - return; - - ScopedErrorReportLock l; - SanitizerCommonDecorator d; - - InternalScopedBuffer stack_buffer(1); - BufferedStackTrace *stack = stack_buffer.data(); - stack->Reset(); - uptr top = 0; - uptr bottom = 0; - GET_CALLER_PC_BP_SP; - (void)sp; - bool fast = common_flags()->fast_unwind_on_fatal; - if (fast) - GetThreadStackTopAndBottom(false, &top, &bottom); - stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, fast); - - Printf("%s", d.Warning()); - Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName); - Printf("%s", d.Default()); - stack->Print(); - ReportErrorSummary("w-and-x-usage", stack); -#endif -} +namespace __sanitizer { static void (*SoftRssLimitExceededCallback)(bool exceeded); void SetSoftRssLimitExceededCallback(void (*Callback)(bool exceeded)) { @@ -178,127 +92,6 @@ } #endif -#if !SANITIZER_FUCHSIA && !SANITIZER_GO -void StartReportDeadlySignal() { - // Write the first message using fd=2, just in case. - // It may actually fail to write in case stderr is closed. - CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName)); - static const char kDeadlySignal[] = ":DEADLYSIGNAL\n"; - CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1); -} - -static void MaybeReportNonExecRegion(uptr pc) { -#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD - MemoryMappingLayout proc_maps(/*cache_enabled*/ true); - MemoryMappedSegment segment; - while (proc_maps.Next(&segment)) { - if (pc >= segment.start && pc < segment.end && !segment.IsExecutable()) - Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n"); - } -#endif -} - -static void PrintMemoryByte(InternalScopedString *str, const char *before, - u8 byte) { - SanitizerCommonDecorator d; - str->append("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15, - d.Default()); -} - -static void MaybeDumpInstructionBytes(uptr pc) { - if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) - return; - InternalScopedString str(1024); - str.append("First 16 instruction bytes at pc: "); - if (IsAccessibleMemoryRange(pc, 16)) { - for (int i = 0; i < 16; ++i) { - PrintMemoryByte(&str, "", ((u8 *)pc)[i]); - } - str.append("\n"); - } else { - str.append("unaccessible\n"); - } - Report("%s", str.data()); -} - -static void MaybeDumpRegisters(void *context) { - if (!common_flags()->dump_registers) return; - SignalContext::DumpAllRegisters(context); -} - -static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid, - UnwindSignalStackCallbackType unwind, - const void *unwind_context) { - SanitizerCommonDecorator d; - Printf("%s", d.Warning()); - static const char kDescription[] = "stack-overflow"; - Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n", - SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc, - (void *)sig.bp, (void *)sig.sp, tid); - Printf("%s", d.Default()); - InternalScopedBuffer stack_buffer(1); - BufferedStackTrace *stack = stack_buffer.data(); - stack->Reset(); - unwind(sig, unwind_context, stack); - stack->Print(); - ReportErrorSummary(kDescription, stack); -} - -static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid, - UnwindSignalStackCallbackType unwind, - const void *unwind_context) { - SanitizerCommonDecorator d; - Printf("%s", d.Warning()); - const char *description = sig.Describe(); - Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n", - SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc, - (void *)sig.bp, (void *)sig.sp, tid); - Printf("%s", d.Default()); - if (sig.pc < GetPageSizeCached()) - Report("Hint: pc points to the zero page.\n"); - if (sig.is_memory_access) { - const char *access_type = - sig.write_flag == SignalContext::WRITE - ? "WRITE" - : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN"); - Report("The signal is caused by a %s memory access.\n", access_type); - if (sig.addr < GetPageSizeCached()) - Report("Hint: address points to the zero page.\n"); - } - MaybeReportNonExecRegion(sig.pc); - InternalScopedBuffer stack_buffer(1); - BufferedStackTrace *stack = stack_buffer.data(); - stack->Reset(); - unwind(sig, unwind_context, stack); - stack->Print(); - MaybeDumpInstructionBytes(sig.pc); - MaybeDumpRegisters(sig.context); - Printf("%s can not provide additional info.\n", SanitizerToolName); - ReportErrorSummary(description, stack); -} - -void ReportDeadlySignal(const SignalContext &sig, u32 tid, - UnwindSignalStackCallbackType unwind, - const void *unwind_context) { - if (sig.IsStackOverflow()) - ReportStackOverflowImpl(sig, tid, unwind, unwind_context); - else - ReportDeadlySignalImpl(sig, tid, unwind, unwind_context); -} - -void HandleDeadlySignal(void *siginfo, void *context, u32 tid, - UnwindSignalStackCallbackType unwind, - const void *unwind_context) { - StartReportDeadlySignal(); - ScopedErrorReportLock rl; - SignalContext sig(siginfo, context); - ReportDeadlySignal(sig, tid, unwind, unwind_context); - Report("ABORTING\n"); - Die(); -} - -#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO - void WriteToSyslog(const char *msg) { InternalScopedString msg_copy(kErrorMessageBufferSize); msg_copy.append("%s", msg); @@ -329,47 +122,6 @@ #endif } -static atomic_uintptr_t reporting_thread = {0}; -static StaticSpinMutex CommonSanitizerReportMutex; - -ScopedErrorReportLock::ScopedErrorReportLock() { - uptr current = GetThreadSelf(); - for (;;) { - uptr expected = 0; - if (atomic_compare_exchange_strong(&reporting_thread, &expected, current, - memory_order_relaxed)) { - // We've claimed reporting_thread so proceed. - CommonSanitizerReportMutex.Lock(); - return; - } - - if (expected == current) { - // This is either asynch signal or nested error during error reporting. - // Fail simple to avoid deadlocks in Report(). - - // Can't use Report() here because of potential deadlocks in nested - // signal handlers. - CatastrophicErrorWrite(SanitizerToolName, - internal_strlen(SanitizerToolName)); - static const char msg[] = ": nested bug in the same thread, aborting.\n"; - CatastrophicErrorWrite(msg, sizeof(msg) - 1); - - internal__exit(common_flags()->exitcode); - } - - internal_sched_yield(); - } -} - -ScopedErrorReportLock::~ScopedErrorReportLock() { - CommonSanitizerReportMutex.Unlock(); - atomic_store_relaxed(&reporting_thread, 0); -} - -void ScopedErrorReportLock::CheckLocked() { - CommonSanitizerReportMutex.CheckLocked(); -} - static void (*sandboxing_callback)(); void SetSandboxingCallback(void (*f)()) { sandboxing_callback = f; Index: lib/sanitizer_common/sanitizer_fuchsia.cc =================================================================== --- lib/sanitizer_common/sanitizer_fuchsia.cc +++ lib/sanitizer_common/sanitizer_fuchsia.cc @@ -18,13 +18,11 @@ #include "sanitizer_common.h" #include "sanitizer_libc.h" #include "sanitizer_mutex.h" -#include "sanitizer_stacktrace.h" #include #include #include #include -#include #include #include #include @@ -92,10 +90,6 @@ void PlatformPrepareForSandboxing(__sanitizer_sandbox_arguments *args) {} void DisableCoreDumperIfNecessary() {} void InstallDeadlySignalHandlers(SignalHandlerType handler) {} -void StartReportDeadlySignal() {} -void ReportDeadlySignal(const SignalContext &sig, u32 tid, - UnwindSignalStackCallbackType unwind, - const void *unwind_context) {} void SetAlternateSignalStack() {} void UnsetAlternateSignalStack() {} void InitTlsSize() {} @@ -106,42 +100,6 @@ void SignalContext::DumpAllRegisters(void *context) { UNIMPLEMENTED(); } const char *SignalContext::Describe() const { UNIMPLEMENTED(); } -struct UnwindTraceArg { - BufferedStackTrace *stack; - u32 max_depth; -}; - -_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { - UnwindTraceArg *arg = static_cast(param); - CHECK_LT(arg->stack->size, arg->max_depth); - uptr pc = _Unwind_GetIP(ctx); - if (pc < PAGE_SIZE) return _URC_NORMAL_STOP; - arg->stack->trace_buffer[arg->stack->size++] = pc; - return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP - : _URC_NO_REASON); -} - -void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { - CHECK_GE(max_depth, 2); - size = 0; - UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; - _Unwind_Backtrace(Unwind_Trace, &arg); - CHECK_GT(size, 0); - // We need to pop a few frames so that pc is on top. - uptr to_pop = LocatePcInTrace(pc); - // trace_buffer[0] belongs to the current function so we always pop it, - // unless there is only 1 frame in the stack trace (1 frame is always better - // than 0!). - PopStackFrames(Min(to_pop, static_cast(1))); - trace_buffer[0] = pc; -} - -void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, - u32 max_depth) { - CHECK_NE(context, nullptr); - UNREACHABLE("signal context doesn't exist"); -} - enum MutexState : int { MtxUnlocked = 0, MtxLocked = 1, MtxSleeping = 2 }; BlockingMutex::BlockingMutex() { Index: lib/sanitizer_common/sanitizer_linux.cc =================================================================== --- lib/sanitizer_common/sanitizer_linux.cc +++ lib/sanitizer_common/sanitizer_linux.cc @@ -14,7 +14,7 @@ #include "sanitizer_platform.h" -#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD || \ SANITIZER_OPENBSD || SANITIZER_SOLARIS #include "sanitizer_common.h" @@ -26,8 +26,6 @@ #include "sanitizer_mutex.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" -#include "sanitizer_stacktrace.h" -#include "sanitizer_symbolizer.h" #if SANITIZER_LINUX #include Index: lib/sanitizer_common/sanitizer_linux_libcdep.cc =================================================================== --- lib/sanitizer_common/sanitizer_linux_libcdep.cc +++ lib/sanitizer_common/sanitizer_linux_libcdep.cc @@ -26,7 +26,6 @@ #include "sanitizer_linux.h" #include "sanitizer_placement_new.h" #include "sanitizer_procmaps.h" -#include "sanitizer_stacktrace.h" #include // for dlsym() #include Index: lib/sanitizer_common/sanitizer_posix.cc =================================================================== --- lib/sanitizer_common/sanitizer_posix.cc +++ lib/sanitizer_common/sanitizer_posix.cc @@ -21,7 +21,6 @@ #include "sanitizer_libc.h" #include "sanitizer_posix.h" #include "sanitizer_procmaps.h" -#include "sanitizer_stacktrace.h" #include #include Index: lib/sanitizer_common/sanitizer_symbolizer_fuchsia.cc =================================================================== --- lib/sanitizer_common/sanitizer_symbolizer_fuchsia.cc +++ lib/sanitizer_common/sanitizer_symbolizer_fuchsia.cc @@ -16,8 +16,12 @@ #if SANITIZER_FUCHSIA #include "sanitizer_fuchsia.h" +#include "sanitizer_stacktrace.h" #include "sanitizer_symbolizer.h" +#include +#include + namespace __sanitizer { // For Fuchsia we don't do any actual symbolization per se. @@ -102,6 +106,47 @@ void Symbolizer::LateInitialize() { Symbolizer::GetOrInit(); } +void StartReportDeadlySignal() {} +void ReportDeadlySignal(const SignalContext &sig, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) {} + +struct UnwindTraceArg { + BufferedStackTrace *stack; + u32 max_depth; +}; + +_Unwind_Reason_Code Unwind_Trace(struct _Unwind_Context *ctx, void *param) { + UnwindTraceArg *arg = static_cast(param); + CHECK_LT(arg->stack->size, arg->max_depth); + uptr pc = _Unwind_GetIP(ctx); + if (pc < PAGE_SIZE) return _URC_NORMAL_STOP; + arg->stack->trace_buffer[arg->stack->size++] = pc; + return (arg->stack->size == arg->max_depth ? _URC_NORMAL_STOP + : _URC_NO_REASON); +} + +void BufferedStackTrace::SlowUnwindStack(uptr pc, u32 max_depth) { + CHECK_GE(max_depth, 2); + size = 0; + UnwindTraceArg arg = {this, Min(max_depth + 1, kStackTraceMax)}; + _Unwind_Backtrace(Unwind_Trace, &arg); + CHECK_GT(size, 0); + // We need to pop a few frames so that pc is on top. + uptr to_pop = LocatePcInTrace(pc); + // trace_buffer[0] belongs to the current function so we always pop it, + // unless there is only 1 frame in the stack trace (1 frame is always better + // than 0!). + PopStackFrames(Min(to_pop, static_cast(1))); + trace_buffer[0] = pc; +} + +void BufferedStackTrace::SlowUnwindStackWithContext(uptr pc, void *context, + u32 max_depth) { + CHECK_NE(context, nullptr); + UNREACHABLE("signal context doesn't exist"); +} + } // namespace __sanitizer #endif // SANITIZER_FUCHSIA Index: lib/sanitizer_common/sanitizer_symbolizer_report.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/sanitizer_symbolizer_report.cc @@ -0,0 +1,268 @@ +#include "sanitizer_common.h" +#include "sanitizer_file.h" +#include "sanitizer_flags.h" +#include "sanitizer_procmaps.h" +#include "sanitizer_report_decorator.h" +#include "sanitizer_stacktrace.h" +#include "sanitizer_stacktrace_printer.h" +#include "sanitizer_symbolizer.h" + +#if SANITIZER_POSIX +# include "sanitizer_posix.h" +# include +#endif + +namespace __sanitizer { + +#if !SANITIZER_GO +void ReportErrorSummary(const char *error_type, const AddressInfo &info, + const char *alt_tool_name) { + if (!common_flags()->print_summary) return; + InternalScopedString buff(kMaxSummaryLength); + buff.append("%s ", error_type); + RenderFrame(&buff, "%L %F", 0, info, common_flags()->symbolize_vs_style, + common_flags()->strip_path_prefix); + ReportErrorSummary(buff.data(), alt_tool_name); +} +#endif + +#if !SANITIZER_FUCHSIA + +bool ReportFile::SupportsColors() { + SpinMutexLock l(mu); + ReopenIfNecessary(); + return SupportsColoredOutput(fd); +} + +static INLINE bool ReportSupportsColors() { + return report_file.SupportsColors(); +} + +#else // SANITIZER_FUCHSIA + +// Fuchsia's logs always go through post-processing that handles colorization. +static INLINE bool ReportSupportsColors() { return true; } + +#endif // !SANITIZER_FUCHSIA + +bool ColorizeReports() { + // FIXME: Add proper Windows support to AnsiColorDecorator and re-enable color + // printing on Windows. + if (SANITIZER_WINDOWS) + return false; + + const char *flag = common_flags()->color; + return internal_strcmp(flag, "always") == 0 || + (internal_strcmp(flag, "auto") == 0 && ReportSupportsColors()); +} + +void ReportErrorSummary(const char *error_type, const StackTrace *stack, + const char *alt_tool_name) { +#if !SANITIZER_GO + if (!common_flags()->print_summary) + return; + if (stack->size == 0) { + ReportErrorSummary(error_type); + return; + } + // Currently, we include the first stack frame into the report summary. + // Maybe sometimes we need to choose another frame (e.g. skip memcpy/etc). + uptr pc = StackTrace::GetPreviousInstructionPc(stack->trace[0]); + SymbolizedStack *frame = Symbolizer::GetOrInit()->SymbolizePC(pc); + ReportErrorSummary(error_type, frame->info, alt_tool_name); + frame->ClearAll(); +#endif +} + +void ReportMmapWriteExec(int prot) { +#if SANITIZER_POSIX && (!SANITIZER_GO && !SANITIZER_ANDROID) + if ((prot & (PROT_WRITE | PROT_EXEC)) != (PROT_WRITE | PROT_EXEC)) + return; + + ScopedErrorReportLock l; + SanitizerCommonDecorator d; + + InternalScopedBuffer stack_buffer(1); + BufferedStackTrace *stack = stack_buffer.data(); + stack->Reset(); + uptr top = 0; + uptr bottom = 0; + GET_CALLER_PC_BP_SP; + (void)sp; + bool fast = common_flags()->fast_unwind_on_fatal; + if (fast) + GetThreadStackTopAndBottom(false, &top, &bottom); + stack->Unwind(kStackTraceMax, pc, bp, nullptr, top, bottom, fast); + + Printf("%s", d.Warning()); + Report("WARNING: %s: writable-executable page usage\n", SanitizerToolName); + Printf("%s", d.Default()); + + stack->Print(); + ReportErrorSummary("w-and-x-usage", stack); +#endif +} + +#if !SANITIZER_FUCHSIA && !SANITIZER_GO +void StartReportDeadlySignal() { + // Write the first message using fd=2, just in case. + // It may actually fail to write in case stderr is closed. + CatastrophicErrorWrite(SanitizerToolName, internal_strlen(SanitizerToolName)); + static const char kDeadlySignal[] = ":DEADLYSIGNAL\n"; + CatastrophicErrorWrite(kDeadlySignal, sizeof(kDeadlySignal) - 1); +} + +static void MaybeReportNonExecRegion(uptr pc) { +#if SANITIZER_FREEBSD || SANITIZER_LINUX || SANITIZER_NETBSD + MemoryMappingLayout proc_maps(/*cache_enabled*/ true); + MemoryMappedSegment segment; + while (proc_maps.Next(&segment)) { + if (pc >= segment.start && pc < segment.end && !segment.IsExecutable()) + Report("Hint: PC is at a non-executable region. Maybe a wild jump?\n"); + } +#endif +} + +static void PrintMemoryByte(InternalScopedString *str, const char *before, + u8 byte) { + SanitizerCommonDecorator d; + str->append("%s%s%x%x%s ", before, d.MemoryByte(), byte >> 4, byte & 15, + d.Default()); +} + +static void MaybeDumpInstructionBytes(uptr pc) { + if (!common_flags()->dump_instruction_bytes || (pc < GetPageSizeCached())) + return; + InternalScopedString str(1024); + str.append("First 16 instruction bytes at pc: "); + if (IsAccessibleMemoryRange(pc, 16)) { + for (int i = 0; i < 16; ++i) { + PrintMemoryByte(&str, "", ((u8 *)pc)[i]); + } + str.append("\n"); + } else { + str.append("unaccessible\n"); + } + Report("%s", str.data()); +} + +static void MaybeDumpRegisters(void *context) { + if (!common_flags()->dump_registers) return; + SignalContext::DumpAllRegisters(context); +} + +static void ReportStackOverflowImpl(const SignalContext &sig, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) { + SanitizerCommonDecorator d; + Printf("%s", d.Warning()); + static const char kDescription[] = "stack-overflow"; + Report("ERROR: %s: %s on address %p (pc %p bp %p sp %p T%d)\n", + SanitizerToolName, kDescription, (void *)sig.addr, (void *)sig.pc, + (void *)sig.bp, (void *)sig.sp, tid); + Printf("%s", d.Default()); + InternalScopedBuffer stack_buffer(1); + BufferedStackTrace *stack = stack_buffer.data(); + stack->Reset(); + unwind(sig, unwind_context, stack); + stack->Print(); + ReportErrorSummary(kDescription, stack); +} + +static void ReportDeadlySignalImpl(const SignalContext &sig, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) { + SanitizerCommonDecorator d; + Printf("%s", d.Warning()); + const char *description = sig.Describe(); + Report("ERROR: %s: %s on unknown address %p (pc %p bp %p sp %p T%d)\n", + SanitizerToolName, description, (void *)sig.addr, (void *)sig.pc, + (void *)sig.bp, (void *)sig.sp, tid); + Printf("%s", d.Default()); + if (sig.pc < GetPageSizeCached()) + Report("Hint: pc points to the zero page.\n"); + if (sig.is_memory_access) { + const char *access_type = + sig.write_flag == SignalContext::WRITE + ? "WRITE" + : (sig.write_flag == SignalContext::READ ? "READ" : "UNKNOWN"); + Report("The signal is caused by a %s memory access.\n", access_type); + if (sig.addr < GetPageSizeCached()) + Report("Hint: address points to the zero page.\n"); + } + MaybeReportNonExecRegion(sig.pc); + InternalScopedBuffer stack_buffer(1); + BufferedStackTrace *stack = stack_buffer.data(); + stack->Reset(); + unwind(sig, unwind_context, stack); + stack->Print(); + MaybeDumpInstructionBytes(sig.pc); + MaybeDumpRegisters(sig.context); + Printf("%s can not provide additional info.\n", SanitizerToolName); + ReportErrorSummary(description, stack); +} + +void ReportDeadlySignal(const SignalContext &sig, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) { + if (sig.IsStackOverflow()) + ReportStackOverflowImpl(sig, tid, unwind, unwind_context); + else + ReportDeadlySignalImpl(sig, tid, unwind, unwind_context); +} + +void HandleDeadlySignal(void *siginfo, void *context, u32 tid, + UnwindSignalStackCallbackType unwind, + const void *unwind_context) { + StartReportDeadlySignal(); + ScopedErrorReportLock rl; + SignalContext sig(siginfo, context); + ReportDeadlySignal(sig, tid, unwind, unwind_context); + Report("ABORTING\n"); + Die(); +} + +#endif // !SANITIZER_FUCHSIA && !SANITIZER_GO + +static atomic_uintptr_t reporting_thread = {0}; +static StaticSpinMutex CommonSanitizerReportMutex; + +ScopedErrorReportLock::ScopedErrorReportLock() { + uptr current = GetThreadSelf(); + for (;;) { + uptr expected = 0; + if (atomic_compare_exchange_strong(&reporting_thread, &expected, current, + memory_order_relaxed)) { + // We've claimed reporting_thread so proceed. + CommonSanitizerReportMutex.Lock(); + return; + } + + if (expected == current) { + // This is either asynch signal or nested error during error reporting. + // Fail simple to avoid deadlocks in Report(). + + // Can't use Report() here because of potential deadlocks in nested + // signal handlers. + CatastrophicErrorWrite(SanitizerToolName, + internal_strlen(SanitizerToolName)); + static const char msg[] = ": nested bug in the same thread, aborting.\n"; + CatastrophicErrorWrite(msg, sizeof(msg) - 1); + + internal__exit(common_flags()->exitcode); + } + + internal_sched_yield(); + } +} + +ScopedErrorReportLock::~ScopedErrorReportLock() { + CommonSanitizerReportMutex.Unlock(); + atomic_store_relaxed(&reporting_thread, 0); +} + +void ScopedErrorReportLock::CheckLocked() { + CommonSanitizerReportMutex.CheckLocked(); +} + +} // namespace __sanitizer Index: lib/sanitizer_common/tests/CMakeLists.txt =================================================================== --- lib/sanitizer_common/tests/CMakeLists.txt +++ lib/sanitizer_common/tests/CMakeLists.txt @@ -178,7 +178,8 @@ if(APPLE) add_sanitizer_common_lib("RTSanitizerCommon.test.osx" $ - $) + $ + $) else() if(CAN_TARGET_x86_64) add_sanitizer_common_lib("RTSanitizerCommon.test.nolibc.x86_64" @@ -188,7 +189,8 @@ foreach(arch ${SANITIZER_UNITTEST_SUPPORTED_ARCH}) add_sanitizer_common_lib("RTSanitizerCommon.test.${arch}" $ - $) + $ + $) endforeach() endif() foreach(arch ${SANITIZER_UNITTEST_SUPPORTED_ARCH}) @@ -202,7 +204,8 @@ ${SANITIZER_UNITTESTS} ${COMPILER_RT_GTEST_SOURCE} $ - $) + $ + $) set_target_compile_flags(SanitizerTest ${SANITIZER_COMMON_CFLAGS} ${SANITIZER_TEST_CFLAGS_COMMON}) Index: lib/scudo/CMakeLists.txt =================================================================== --- lib/scudo/CMakeLists.txt +++ lib/scudo/CMakeLists.txt @@ -25,10 +25,11 @@ if (FUCHSIA) list(APPEND SCUDO_CFLAGS -nostdinc++) list(APPEND SCUDO_DYNAMIC_LINK_FLAGS -nostdlib++) - # TODO(kostyak): remove when stacktraces are split off of RTSanitizerCommon - list(APPEND SCUDO_DYNAMIC_LIBS unwind_shared) else() - list(APPEND SCUDO_OBJECT_LIBS RTSanitizerCommonCoverage RTUbsan) + list(APPEND SCUDO_OBJECT_LIBS + RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer + RTUbsan) list(APPEND SCUDO_DYNAMIC_LIBS ${SANITIZER_CXX_ABI_LIBRARY}) endif() Index: lib/tsan/CMakeLists.txt =================================================================== --- lib/tsan/CMakeLists.txt +++ lib/tsan/CMakeLists.txt @@ -115,6 +115,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTUbsan CFLAGS ${TSAN_RTL_CFLAGS} LINK_FLAGS ${SANITIZER_COMMON_LINK_FLAGS} ${WEAK_SYMBOL_LINK_FLAGS} @@ -165,6 +166,7 @@ $ $ $ + $ $ CFLAGS ${TSAN_RTL_CFLAGS} PARENT_TARGET tsan) Index: lib/tsan/go/buildgo.sh =================================================================== --- lib/tsan/go/buildgo.sh +++ lib/tsan/go/buildgo.sh @@ -35,6 +35,7 @@ ../../sanitizer_common/sanitizer_stackdepot.cc ../../sanitizer_common/sanitizer_stacktrace.cc ../../sanitizer_common/sanitizer_symbolizer.cc + ../../sanitizer_common/sanitizer_symbolizer_report.cc ../../sanitizer_common/sanitizer_termination.cc " Index: lib/tsan/tests/CMakeLists.txt =================================================================== --- lib/tsan/tests/CMakeLists.txt +++ lib/tsan/tests/CMakeLists.txt @@ -23,6 +23,7 @@ $ $ $ + $ $) set(TSAN_TEST_RUNTIME RTTsanTest) add_library(${TSAN_TEST_RUNTIME} STATIC ${TSAN_TEST_RUNTIME_OBJECTS}) Index: lib/ubsan/CMakeLists.txt =================================================================== --- lib/ubsan/CMakeLists.txt +++ lib/ubsan/CMakeLists.txt @@ -77,6 +77,7 @@ RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTInterception LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS} PARENT_TARGET ubsan) @@ -90,6 +91,7 @@ RTSanitizerCommonNoHooks RTSanitizerCommonLibcNoHooks RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTInterception LINK_FLAGS ${WEAK_SYMBOL_LINK_FLAGS} PARENT_TARGET ubsan) @@ -157,6 +159,7 @@ OBJECT_LIBS RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTUbsan RTUbsan_standalone RTInterception @@ -177,6 +180,7 @@ OBJECT_LIBS RTSanitizerCommon RTSanitizerCommonLibc RTSanitizerCommonCoverage + RTSanitizerCommonSymbolizer RTUbsan RTUbsan_cxx RTUbsan_standalone