//===--- Backtrace.cpp - Swift crash catching and backtracing support ---- ===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2022 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // Crash catching and backtracing support routines. // //===----------------------------------------------------------------------===// #include #include "llvm/ADT/StringRef.h" #include "swift/Runtime/Config.h" #include "swift/Runtime/Backtrace.h" #include "swift/Runtime/Debug.h" #include "swift/Runtime/Paths.h" #include "swift/Runtime/EnvironmentVariables.h" #include "swift/Runtime/Win32.h" #include "swift/Demangling/Demangler.h" #ifdef __linux__ #include #endif #ifdef _WIN32 #include #else #if __has_include() #include #define PROTECT_BACKTRACE_SETTINGS 1 #else #define PROTECT_BACKTRACE_SETTINGS 0 #warning Backtracer settings will not be protected in this configuration. #endif #if TARGET_OS_OSX || TARGET_OS_MACCATALYST #if __has_include() #include #else // SPI #define CS_OPS_STATUS 0 #define CS_GET_TASK_ALLOW 0x00000004 #define CS_RUNTIME 0x00010000 #define CS_PLATFORM_BINARY 0x04000000 #define CS_PLATFORM_PATH 0x08000000 extern "C" int csops(int, unsigned int, void *, size_t); #endif #include #endif #include #endif #include #include #include #ifdef _WIN32 // We'll probably want dbghelp.h here #else #include #endif #include "BacktracePrivate.h" #define DEBUG_BACKTRACING_SETTINGS 0 #ifndef lengthof #define lengthof(x) (sizeof(x) / sizeof(x[0])) #endif using namespace swift::runtime::backtrace; namespace swift { namespace runtime { namespace backtrace { SWIFT_RUNTIME_STDLIB_INTERNAL BacktraceSettings _swift_backtraceSettings = { UnwindAlgorithm::Auto, // enabled OnOffTty::Default, // demangle true, // interactive #if TARGET_OS_OSX || defined(__linux__) // || defined(_WIN32) OnOffTty::TTY, #else OnOffTty::Off, #endif // color OnOffTty::TTY, // timeout 30, // threads ThreadsToShow::Preset, // registers RegistersToShow::Preset, // images ImagesToShow::Preset, // limit 64, // top 16, // sanitize SanitizePaths::Preset, // preset Preset::Auto, // cache true, // outputTo OutputTo::Auto, // symbolicate Symbolication::Full, // suppressWarnings false, // format OutputFormat::Text, // swiftBacktracePath NULL, // outputPath NULL, }; } } } namespace { class BacktraceInitializer { public: BacktraceInitializer(); }; SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_BEGIN BacktraceInitializer backtraceInitializer; SWIFT_ALLOWED_RUNTIME_GLOBAL_CTOR_END #if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED // We need swiftBacktracePath to be aligned on a page boundary, and it also // needs to be a multiple of the system page size. #define SWIFT_BACKTRACE_BUFFER_SIZE 16384 static_assert((SWIFT_BACKTRACE_BUFFER_SIZE % SWIFT_PAGE_SIZE) == 0, "The backtrace path buffer must be a multiple of the system " "page size. If it isn't, you'll get weird crashes in other " "code because we'll protect more than just the buffer."); // The same goes for swiftBacktraceEnvironment #define SWIFT_BACKTRACE_ENVIRONMENT_SIZE 32768 static_assert((SWIFT_BACKTRACE_ENVIRONMENT_SIZE % SWIFT_PAGE_SIZE) == 0, "The environment buffer must be a multiple of the system " "page size. If it isn't, you'll get weird crashes in other " "code because we'll protect more than just the buffer."); // And the output path #define SWIFT_BACKTRACE_OUTPUT_PATH_SIZE 16384 static_assert((SWIFT_BACKTRACE_OUTPUT_PATH_SIZE % SWIFT_PAGE_SIZE) == 0, "The output path buffer must be a multiple of the system " "page size. If it isn't, you'll get weird crashes in other " "code because we'll protect more than just the buffer."); #if _WIN32 #pragma section(SWIFT_BACKTRACE_SECTION, read, write) #if defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) const WCHAR swiftBacktracePath[] = L"" SWIFT_RUNTIME_FIXED_BACKTRACER_PATH; #else __declspec(allocate(SWIFT_BACKTRACE_SECTION)) WCHAR swiftBacktracePath[SWIFT_BACKTRACE_BUFFER_SIZE]; #endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) __declspec(allocate(SWIFT_BACKTRACE_SECTION)) CHAR swiftBacktraceEnv[SWIFT_BACKTRACE_ENVIRONMENT_SIZE]; __declspec(allocate(SWIFT_BACKTRACE_SECTION)) CHAR swiftBacktraceOutputPath[SWIFT_BACKTRACE_OUTPUT_PATH_SIZE]; #elif defined(__linux__) || TARGET_OS_OSX #if defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) const char swiftBacktracePath[] = SWIFT_RUNTIME_FIXED_BACKTRACER_PATH; #else char swiftBacktracePath[SWIFT_BACKTRACE_BUFFER_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE))); #endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH char swiftBacktraceEnv[SWIFT_BACKTRACE_ENVIRONMENT_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE))); char swiftBacktraceOutputPath[SWIFT_BACKTRACE_OUTPUT_PATH_SIZE] __attribute__((section(SWIFT_BACKTRACE_SECTION), aligned(SWIFT_PAGE_SIZE))); #endif // defined(__linux__) || TARGET_OS_OSX void _swift_backtraceSetupEnvironment(); bool isStdoutATty() { #ifndef _WIN32 return isatty(STDOUT_FILENO); #else DWORD dwMode; return GetConsoleMode(GetStdHandle(STD_OUTPUT_HANDLE), &dwMode); #endif } bool isStdinATty() { #ifndef _WIN32 return isatty(STDIN_FILENO); #else DWORD dwMode; return GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &dwMode); #endif } #endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED void _swift_processBacktracingSetting(llvm::StringRef key, llvm::StringRef value); void _swift_parseBacktracingSettings(const char *); #if DEBUG_BACKTRACING_SETTINGS const char *algorithmToString(UnwindAlgorithm algorithm) { switch (algorithm) { case UnwindAlgorithm::Auto: return "Auto"; case UnwindAlgorithm::Fast: return "Fast"; case UnwindAlgorithm::Precise: return "Precise"; } } const char *onOffTtyToString(OnOffTty oot) { switch (oot) { case OnOffTty::Default: return "Default"; case OnOffTty::On: return "On"; case OnOffTty::Off: return "Off"; case OnOffTty::TTY: return "TTY"; } } const char *boolToString(bool b) { return b ? "true" : "false"; } const char *presetToString(Preset preset) { switch (preset) { case Preset::Auto: return "Auto"; case Preset::Friendly: return "Friendly"; case Preset::Medium: return "Medium"; case Preset::Full: return Full; } } #endif #ifdef __linux__ bool isPrivileged() { return getauxval(AT_SECURE); } #elif TARGET_OS_OSX || TARGET_OS_MACCATALYST bool isPrivileged() { if (issetugid()) return true; uint32_t flags = 0; if (csops(getpid(), CS_OPS_STATUS, &flags, sizeof(flags)) != 0) return true; if (flags & (CS_PLATFORM_BINARY | CS_PLATFORM_PATH | CS_RUNTIME)) return true; return !(flags & CS_GET_TASK_ALLOW); } #elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) bool isPrivileged() { return issetugid(); } #elif _WIN32 bool isPrivileged() { return false; } #endif #if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED #if _WIN32 bool writeProtectMemory(void *ptr, size_t size) { return !!VirtualProtect(ptr, size, PAGE_READONLY, NULL); } #else bool writeProtectMemory(void *ptr, size_t size) { return mprotect(ptr, size, PROT_READ) == 0; } #endif #endif } // namespace BacktraceInitializer::BacktraceInitializer() { const char *backtracing = swift::runtime::environment::SWIFT_BACKTRACE(); // Force off for setuid processes. if (isPrivileged()) { _swift_backtraceSettings.enabled = OnOffTty::Off; } if (backtracing) _swift_parseBacktracingSettings(backtracing); #if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) if (!_swift_backtraceSettings.swiftBacktracePath) { _swift_backtraceSettings.swiftBacktracePath = swift_copyAuxiliaryExecutablePath("swift-backtrace"); if (!_swift_backtraceSettings.swiftBacktracePath) { if (_swift_backtraceSettings.enabled == OnOffTty::On) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unable to locate swift-backtrace; " "disabling backtracing.\n"); } } _swift_backtraceSettings.enabled = OnOffTty::Off; } } #endif if (_swift_backtraceSettings.enabled == OnOffTty::Default) { #if TARGET_OS_OSX _swift_backtraceSettings.enabled = OnOffTty::TTY; #elif defined(__linux__) // || defined(_WIN32) _swift_backtraceSettings.enabled = OnOffTty::On; #else _swift_backtraceSettings.enabled = OnOffTty::Off; #endif } #if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED if (_swift_backtraceSettings.enabled != OnOffTty::Off) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: backtrace-on-crash is not supported on " "this platform.\n"); } _swift_backtraceSettings.enabled = OnOffTty::Off; } #else if (isPrivileged() && _swift_backtraceSettings.enabled != OnOffTty::Off) { // You'll only see this warning if you do e.g. // // SWIFT_BACKTRACE=enable=on /path/to/some/setuid/binary // // as opposed to // // /path/to/some/setuid/binary // // i.e. when you're trying to force matters. if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: backtrace-on-crash is not supported for " "privileged executables.\n"); } _swift_backtraceSettings.enabled = OnOffTty::Off; } // If we're outputting to a file, then the defaults are different if (_swift_backtraceSettings.outputTo == OutputTo::File) { if (_swift_backtraceSettings.interactive == OnOffTty::TTY) _swift_backtraceSettings.interactive = OnOffTty::Off; if (_swift_backtraceSettings.color == OnOffTty::TTY) _swift_backtraceSettings.color = OnOffTty::Off; // Unlike the other settings, this defaults to on if you specified a file if (_swift_backtraceSettings.enabled == OnOffTty::TTY) _swift_backtraceSettings.enabled = OnOffTty::On; } if (_swift_backtraceSettings.enabled == OnOffTty::TTY) _swift_backtraceSettings.enabled = isStdoutATty() ? OnOffTty::On : OnOffTty::Off; if (_swift_backtraceSettings.interactive == OnOffTty::TTY) { _swift_backtraceSettings.interactive = (isStdoutATty() && isStdinATty()) ? OnOffTty::On : OnOffTty::Off; } if (_swift_backtraceSettings.color == OnOffTty::TTY) _swift_backtraceSettings.color = isStdoutATty() ? OnOffTty::On : OnOffTty::Off; if (_swift_backtraceSettings.preset == Preset::Auto) { if (_swift_backtraceSettings.interactive == OnOffTty::On) _swift_backtraceSettings.preset = Preset::Friendly; else _swift_backtraceSettings.preset = Preset::Full; } if (_swift_backtraceSettings.outputTo == OutputTo::File) { size_t len = strlen(_swift_backtraceSettings.outputPath); if (len > SWIFT_BACKTRACE_OUTPUT_PATH_SIZE - 1) { swift::warning(0, "swift runtime: backtracer output path too long; output " "path setting will be ignored.\n"); _swift_backtraceSettings.outputTo = OutputTo::Auto; } else { memcpy(swiftBacktraceOutputPath, _swift_backtraceSettings.outputPath, len + 1); #if PROTECT_BACKTRACE_SETTINGS if (!writeProtectMemory(swiftBacktraceOutputPath, sizeof(swiftBacktraceOutputPath))) { swift::warning(0, "swift runtime: unable to protect backtracer output " "path; path setting will be ignored.\n"); _swift_backtraceSettings.outputTo = OutputTo::Auto; } #endif } } if (_swift_backtraceSettings.outputTo == OutputTo::Auto) { if (_swift_backtraceSettings.interactive == OnOffTty::On) _swift_backtraceSettings.outputTo = OutputTo::Stdout; else _swift_backtraceSettings.outputTo = OutputTo::Stderr; } if (_swift_backtraceSettings.enabled == OnOffTty::On) { // Copy the path to swift-backtrace into swiftBacktracePath, then write // protect it so that it can't be overwritten easily at runtime. We do // this to avoid creating a massive security hole that would allow an // attacker to overwrite the path and then cause a crash to get us to // execute an arbitrary file. if (_swift_backtraceSettings.algorithm == UnwindAlgorithm::Auto) _swift_backtraceSettings.algorithm = UnwindAlgorithm::Precise; #if _WIN32 #if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) int len = ::MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, _swift_backtraceSettings.swiftBacktracePath, -1, swiftBacktracePath, SWIFT_BACKTRACE_BUFFER_SIZE); if (!len) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unable to convert path to " "swift-backtrace: %08lx; disabling backtracing.\n", ::GetLastError()); } _swift_backtraceSettings.enabled = OnOffTty::Off; } #endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) #else // !_WIN32 #if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) size_t len = strlen(_swift_backtraceSettings.swiftBacktracePath); if (len > SWIFT_BACKTRACE_BUFFER_SIZE - 1) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: path to swift-backtrace is too long; " "disabling backtracing.\n"); } _swift_backtraceSettings.enabled = OnOffTty::Off; } else { memcpy(swiftBacktracePath, _swift_backtraceSettings.swiftBacktracePath, len + 1); } #endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) #endif // !_WIN32 _swift_backtraceSetupEnvironment(); #if PROTECT_BACKTRACE_SETTINGS #if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) if (!writeProtectMemory(swiftBacktracePath, sizeof(swiftBacktracePath))) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unable to protect path to " "swift-backtrace at %p; disabling backtracing.\n", swiftBacktracePath); } _swift_backtraceSettings.enabled = OnOffTty::Off; } #endif if (!writeProtectMemory(swiftBacktraceEnv, sizeof(swiftBacktraceEnv))) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unable to protect environment " "for swift-backtrace at %p; disabling backtracing.\n", swiftBacktraceEnv); } _swift_backtraceSettings.enabled = OnOffTty::Off; } #endif } if (_swift_backtraceSettings.enabled == OnOffTty::On) { ErrorCode err = _swift_installCrashHandler(); if (err != 0) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: crash handler installation failed; " "disabling backtracing.\n"); } } } #endif #if DEBUG_BACKTRACING_SETTINGS printf("\nBACKTRACING SETTINGS\n" "\n" "algorithm: %s\n" "enabled: %s\n" "demangle: %s\n" "interactive: %s\n" "color: %s\n" "timeout: %u\n" "preset: %s\n" "swiftBacktracePath: %s\n", algorithmToString(_swift_backtraceSettings.algorithm), onOffTtyToString(_swift_backtraceSettings.enabled), boolToString(_swift_backtraceSettings.demangle), onOffTtyToString(_swift_backtraceSettings.interactive), onOffTtyToString(_swift_backtraceSettings.color), _swift_backtraceSettings.timeout, presetToString(_swift_backtraceSettings.preset), swiftBacktracePath); printf("\nBACKTRACING ENV\n"); const char *ptr = swiftBacktraceEnv; while (*ptr) { size_t len = std::strlen(ptr); printf("%s\n", ptr); ptr += len + 1; } printf("\n"); #endif } namespace { OnOffTty parseOnOffTty(llvm::StringRef value) { if (value.equals_insensitive("on") || value.equals_insensitive("true") || value.equals_insensitive("yes") || value.equals_insensitive("y") || value.equals_insensitive("t") || value.equals_insensitive("1")) return OnOffTty::On; if (value.equals_insensitive("tty") || value.equals_insensitive("auto")) return OnOffTty::TTY; return OnOffTty::Off; } bool parseBoolean(llvm::StringRef value) { return (value.equals_insensitive("on") || value.equals_insensitive("true") || value.equals_insensitive("yes") || value.equals_insensitive("y") || value.equals_insensitive("t") || value.equals_insensitive("1")); } Symbolication parseSymbolication(llvm::StringRef value) { if (value.equals_insensitive("on") || value.equals_insensitive("true") || value.equals_insensitive("yes") || value.equals_insensitive("y") || value.equals_insensitive("t") || value.equals_insensitive("1") || value.equals_insensitive("full")) return Symbolication::Full; if (value.equals_insensitive("fast")) return Symbolication::Fast; return Symbolication::Off; } void _swift_processBacktracingSetting(llvm::StringRef key, llvm::StringRef value) { if (key.equals_insensitive("enable")) { _swift_backtraceSettings.enabled = parseOnOffTty(value); } else if (key.equals_insensitive("demangle")) { _swift_backtraceSettings.demangle = parseBoolean(value); } else if (key.equals_insensitive("interactive")) { _swift_backtraceSettings.interactive = parseOnOffTty(value); } else if (key.equals_insensitive("color")) { _swift_backtraceSettings.color = parseOnOffTty(value); } else if (key.equals_insensitive("timeout")) { int count; llvm::StringRef valueCopy = value; if (value.equals_insensitive("none")) { _swift_backtraceSettings.timeout = 0; } else if (!valueCopy.consumeInteger(0, count)) { // Yes, consumeInteger() really does return *false* for success llvm::StringRef unit = valueCopy.trim(); if (unit.empty() || unit.equals_insensitive("s") || unit.equals_insensitive("seconds")) _swift_backtraceSettings.timeout = count; else if (unit.equals_insensitive("m") || unit.equals_insensitive("minutes")) _swift_backtraceSettings.timeout = count * 60; else if (unit.equals_insensitive("h") || unit.equals_insensitive("hours")) _swift_backtraceSettings.timeout = count * 3600; if (_swift_backtraceSettings.timeout < 0) { if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: bad backtracing timeout %ds\n", _swift_backtraceSettings.timeout); } _swift_backtraceSettings.timeout = 0; } } else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: bad backtracing timeout '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("unwind")) { if (value.equals_insensitive("auto")) _swift_backtraceSettings.algorithm = UnwindAlgorithm::Auto; else if (value.equals_insensitive("fast")) _swift_backtraceSettings.algorithm = UnwindAlgorithm::Fast; else if (value.equals_insensitive("precise")) _swift_backtraceSettings.algorithm = UnwindAlgorithm::Precise; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown unwind algorithm '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("sanitize")) { _swift_backtraceSettings.sanitize = parseBoolean(value) ? SanitizePaths::On : SanitizePaths::Off; } else if (key.equals_insensitive("preset")) { if (value.equals_insensitive("auto")) _swift_backtraceSettings.preset = Preset::Auto; else if (value.equals_insensitive("friendly")) _swift_backtraceSettings.preset = Preset::Friendly; else if (value.equals_insensitive("medium")) _swift_backtraceSettings.preset = Preset::Medium; else if (value.equals_insensitive("full")) _swift_backtraceSettings.preset = Preset::Full; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown backtracing preset '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("threads")) { if (value.equals_insensitive("all")) _swift_backtraceSettings.threads = ThreadsToShow::All; else if (value.equals_insensitive("crashed")) _swift_backtraceSettings.threads = ThreadsToShow::Crashed; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown threads setting '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("registers")) { if (value.equals_insensitive("none")) _swift_backtraceSettings.registers = RegistersToShow::None; else if (value.equals_insensitive("all")) _swift_backtraceSettings.registers = RegistersToShow::All; else if (value.equals_insensitive("crashed")) _swift_backtraceSettings.registers = RegistersToShow::Crashed; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown registers setting '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("images")) { if (value.equals_insensitive("none")) _swift_backtraceSettings.images = ImagesToShow::None; else if (value.equals_insensitive("all")) _swift_backtraceSettings.images = ImagesToShow::All; else if (value.equals_insensitive("mentioned")) _swift_backtraceSettings.images = ImagesToShow::Mentioned; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown registers setting '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("limit")) { int limit; // Yes, getAsInteger() returns false for success. if (value.equals_insensitive("none")) _swift_backtraceSettings.limit = -1; else if (!value.getAsInteger(0, limit) && limit > 0) _swift_backtraceSettings.limit = limit; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: bad backtrace limit '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("top")) { int top; // (If you think the next line is wrong, see above.) if (!value.getAsInteger(0, top) && top >= 0) _swift_backtraceSettings.top = top; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: bad backtrace top count '%.*s'\n", static_cast(value.size()), value.data()); } } else if (key.equals_insensitive("cache")) { _swift_backtraceSettings.cache = parseBoolean(value); } else if (key.equals_insensitive("output-to")) { if (value.equals_insensitive("auto")) _swift_backtraceSettings.outputTo = OutputTo::Auto; else if (value.equals_insensitive("stdout")) _swift_backtraceSettings.outputTo = OutputTo::Stdout; else if (value.equals_insensitive("stderr")) _swift_backtraceSettings.outputTo = OutputTo::Stderr; else { size_t len = value.size(); char *path = (char *)std::malloc(len + 1); std::copy(value.begin(), value.end(), path); path[len] = 0; std::free(const_cast(_swift_backtraceSettings.outputPath)); _swift_backtraceSettings.outputTo = OutputTo::File; _swift_backtraceSettings.outputPath = path; } } else if (key.equals_insensitive("symbolicate")) { _swift_backtraceSettings.symbolicate = parseSymbolication(value); } else if (key.equals_insensitive("format")) { if (value.equals_insensitive("text")) { _swift_backtraceSettings.format = OutputFormat::Text; } else if (value.equals_insensitive("json")) { _swift_backtraceSettings.format = OutputFormat::JSON; } else { swift::warning(0, "swift runtime: unknown backtrace format '%.*s'\n", static_cast(value.size()), value.data()); } #if !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) } else if (key.equals_insensitive("swift-backtrace")) { size_t len = value.size(); char *path = (char *)std::malloc(len + 1); std::copy(value.begin(), value.end(), path); path[len] = 0; std::free(const_cast(_swift_backtraceSettings.swiftBacktracePath)); _swift_backtraceSettings.swiftBacktracePath = path; #endif // !defined(SWIFT_RUNTIME_FIXED_BACKTRACER_PATH) } else if (key.equals_insensitive("warnings")) { if (value.equals_insensitive("suppressed") || value.equals_insensitive("disabled") || value.equals_insensitive("off")) _swift_backtraceSettings.suppressWarnings = true; else if (value.equals_insensitive("enabled") || value.equals_insensitive("on")) _swift_backtraceSettings.suppressWarnings = false; else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown warnings setting '%.*s'\n", static_cast(value.size()), value.data()); } } else if (!_swift_backtraceSettings.suppressWarnings) { swift::warning(0, "swift runtime: unknown backtracing setting '%.*s'\n", static_cast(key.size()), key.data()); } } void _swift_parseBacktracingSettings(const char *settings) { const char *ptr = settings; const char *key = ptr; const char *keyEnd; const char *value; const char *valueEnd; enum { ScanningKey, ScanningValue } state = ScanningKey; int ch; while ((ch = *ptr++)) { switch (state) { case ScanningKey: if (ch == '=') { keyEnd = ptr - 1; value = ptr; state = ScanningValue; continue; } break; case ScanningValue: if (ch == ',') { valueEnd = ptr - 1; _swift_processBacktracingSetting(llvm::StringRef(key, keyEnd - key), llvm::StringRef(value, valueEnd - value)); key = ptr; state = ScanningKey; continue; } break; } } if (state == ScanningValue) { valueEnd = ptr - 1; _swift_processBacktracingSetting(llvm::StringRef(key, keyEnd - key), llvm::StringRef(value, valueEnd - value)); } } #if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED // These are the only environment variables that are passed through to // the swift-backtrace process. They're copied at program start, and then // write protected so they can't be manipulated by an attacker using a buffer // overrun. const char * const environmentVarsToPassThrough[] = { "LD_LIBRARY_PATH", "DYLD_LIBRARY_PATH", "DYLD_FRAMEWORK_PATH", "PATH", "TERM", "LANG", "HOME" }; #define BACKTRACE_MAX_ENV_VARS lengthof(environmentVarsToPassThrough) void _swift_backtraceSetupEnvironment() { size_t remaining = sizeof(swiftBacktraceEnv); char *penv = swiftBacktraceEnv; std::memset(swiftBacktraceEnv, 0, sizeof(swiftBacktraceEnv)); // We definitely don't want this on in the swift-backtrace program const char * const disable = "SWIFT_BACKTRACE=enable=no"; const size_t disableLen = std::strlen(disable) + 1; std::memcpy(penv, disable, disableLen); penv += disableLen; remaining -= disableLen; for (unsigned n = 0; n < BACKTRACE_MAX_ENV_VARS; ++n) { const char *name = environmentVarsToPassThrough[n]; const char *value = getenv(name); if (!value) continue; size_t nameLen = std::strlen(name); size_t valueLen = std::strlen(value); size_t totalLen; if (__builtin_add_overflow(nameLen + 1, valueLen + 1, &totalLen)) { break; } if (remaining > totalLen) { std::memcpy(penv, name, nameLen); penv += nameLen; *penv++ = '='; std::memcpy(penv, value, valueLen); penv += valueLen; *penv++ = 0; remaining -= totalLen; } } *penv = 0; } #ifdef __linux__ struct spawn_info { const char *path; char * const *argv; char * const *envp; int memserver; }; uint8_t spawn_stack[4096] __attribute__((aligned(SWIFT_PAGE_SIZE))); int do_spawn(void *ptr) { struct spawn_info *pinfo = (struct spawn_info *)ptr; /* Ensure that the memory server is always on fd 4 */ if (pinfo->memserver != 4) { dup2(pinfo->memserver, 4); close(pinfo->memserver); } /* Clear the signal mask */ sigset_t mask; sigfillset(&mask); sigprocmask(SIG_UNBLOCK, &mask, NULL); return execvpe(pinfo->path, pinfo->argv, pinfo->envp); } int safe_spawn(pid_t *ppid, const char *path, int memserver, char * const argv[], char * const envp[]) { struct spawn_info info = { path, argv, envp, memserver }; /* The CLONE_VFORK is *required* because info is on the stack; we don't want to return until *after* the subprocess has called execvpe(). */ int ret = clone(do_spawn, spawn_stack + sizeof(spawn_stack), CLONE_VFORK|CLONE_VM, &info); if (ret < 0) return ret; close(memserver); *ppid = ret; return 0; } #endif // defined(__linux__) #endif // SWIFT_BACKTRACE_ON_CRASH_SUPPORTED } // namespace namespace swift { namespace runtime { namespace backtrace { /// Test if a Swift symbol name represents a thunk function. /// /// In backtraces, it is often desirable to omit thunk frames as they usually /// just clutter up the backtrace unnecessarily. /// /// @param mangledName is the symbol name to be tested. /// /// @returns `true` if `mangledName` represents a thunk function. SWIFT_RUNTIME_STDLIB_SPI bool _swift_backtrace_isThunkFunction(const char *mangledName) { swift::Demangle::Context ctx; return ctx.isThunkSymbol(mangledName); } // Try to demangle a symbol. SWIFT_RUNTIME_STDLIB_SPI char * _swift_backtrace_demangle(const char *mangledName, size_t mangledNameLength, char *outputBuffer, size_t *outputBufferSize) { llvm::StringRef name = llvm::StringRef(mangledName, mangledNameLength); // You must provide buffer size if you're providing your own output buffer if (outputBuffer && !outputBufferSize) { return nullptr; } if (Demangle::isSwiftSymbol(name)) { // This is a Swift mangling auto options = DemangleOptions::SimplifiedUIDemangleOptions(); auto result = Demangle::demangleSymbolAsString(name, options); size_t bufferSize; if (outputBufferSize) { bufferSize = *outputBufferSize; *outputBufferSize = result.length() + 1; } if (outputBuffer == nullptr) { outputBuffer = (char *)::malloc(result.length() + 1); bufferSize = result.length() + 1; } size_t toCopy = std::min(bufferSize - 1, result.length()); ::memcpy(outputBuffer, result.data(), toCopy); outputBuffer[toCopy] = '\0'; return outputBuffer; #ifndef _WIN32 } else if (name.starts_with("_Z")) { // Try C++; note that we don't want to force callers to use malloc() to // allocate their buffer, which is a requirement for __cxa_demangle // because it may call realloc() on the incoming pointer. As a result, // we never pass the caller's buffer to __cxa_demangle. size_t resultLen; int status = 0; char *result = abi::__cxa_demangle(mangledName, nullptr, &resultLen, &status); if (result) { size_t bufferSize; if (outputBufferSize) { bufferSize = *outputBufferSize; *outputBufferSize = resultLen; } if (outputBuffer == nullptr) { return result; } size_t toCopy = std::min(bufferSize - 1, resultLen - 1); ::memcpy(outputBuffer, result, toCopy); outputBuffer[toCopy] = '\0'; free(result); return outputBuffer; } #else // On Windows, the mangling is different. // ###TODO: Call __unDName() #endif } return nullptr; } #if SWIFT_BACKTRACE_ON_CRASH_SUPPORTED namespace { char addr_buf[18]; char timeout_buf[22]; char limit_buf[22]; char top_buf[22]; const char *backtracer_argv[] = { "swift-backtrace", // 0 "--unwind", // 1 "precise", // 2 "--demangle", // 3 "true", // 4 "--interactive", // 5 "true", // 6 "--color", // 7 "true", // 8 "--timeout", // 9 timeout_buf, // 10 "--preset", // 11 "friendly", // 12 "--crashinfo", // 13 addr_buf, // 14 "--threads", // 15 "preset", // 16 "--registers", // 17 "preset", // 18 "--images", // 19 "preset", // 20 "--limit", // 21 limit_buf, // 22 "--top", // 23 top_buf, // 24 "--sanitize", // 25 "preset", // 26 "--cache", // 27 "true", // 28 "--output-to", // 29 "stdout", // 30 "--symbolicate", // 31 "full", // 32 "--format", // 33 "text", // 34 NULL }; const char * trueOrFalse(bool b) { return b ? "true" : "false"; } const char * trueOrFalse(OnOffTty oot) { return trueOrFalse(oot == OnOffTty::On); } } // namespace #endif // N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux // and macOS, that means it must be async-signal-safe. On Windows, there // isn't an equivalent notion but a similar restriction applies. SWIFT_RUNTIME_STDLIB_INTERNAL bool #ifdef __linux__ _swift_spawnBacktracer(CrashInfo *crashInfo, int memserver_fd) #else _swift_spawnBacktracer(CrashInfo *crashInfo) #endif { #if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED return false; #elif TARGET_OS_OSX || TARGET_OS_MACCATALYST || defined(__linux__) // Set-up the backtracer's command line arguments switch (_swift_backtraceSettings.algorithm) { case UnwindAlgorithm::Fast: backtracer_argv[2] = "fast"; break; default: backtracer_argv[2] = "precise"; break; } // (The TTY option has already been handled at this point, so these are // all either "On" or "Off".) backtracer_argv[4] = trueOrFalse(_swift_backtraceSettings.demangle); backtracer_argv[6] = trueOrFalse(_swift_backtraceSettings.interactive); backtracer_argv[8] = trueOrFalse(_swift_backtraceSettings.color); switch (_swift_backtraceSettings.threads) { case ThreadsToShow::Preset: backtracer_argv[16] = "preset"; break; case ThreadsToShow::All: backtracer_argv[16] = "all"; break; case ThreadsToShow::Crashed: backtracer_argv[16] = "crashed"; break; } switch (_swift_backtraceSettings.registers) { case RegistersToShow::Preset: backtracer_argv[18] = "preset"; break; case RegistersToShow::None: backtracer_argv[18] = "none"; break; case RegistersToShow::All: backtracer_argv[18] = "all"; break; case RegistersToShow::Crashed: backtracer_argv[18] = "crashed"; break; } switch (_swift_backtraceSettings.images) { case ImagesToShow::Preset: backtracer_argv[20] = "preset"; break; case ImagesToShow::None: backtracer_argv[20] = "none"; break; case ImagesToShow::All: backtracer_argv[20] = "all"; break; case ImagesToShow::Mentioned: backtracer_argv[20] = "mentioned"; break; } switch (_swift_backtraceSettings.preset) { case Preset::Friendly: backtracer_argv[12] = "friendly"; break; case Preset::Medium: backtracer_argv[12] = "medium"; break; default: backtracer_argv[12] = "full"; break; } switch (_swift_backtraceSettings.sanitize) { case SanitizePaths::Preset: backtracer_argv[26] = "preset"; break; case SanitizePaths::Off: backtracer_argv[26] = "false"; break; case SanitizePaths::On: backtracer_argv[26] = "true"; break; } switch (_swift_backtraceSettings.outputTo) { case OutputTo::Stdout: backtracer_argv[30] = "stdout"; break; case OutputTo::Auto: // Shouldn't happen, but if it does pick stderr case OutputTo::Stderr: backtracer_argv[30] = "stderr"; break; case OutputTo::File: backtracer_argv[30] = swiftBacktraceOutputPath; break; } backtracer_argv[28] = trueOrFalse(_swift_backtraceSettings.cache); switch (_swift_backtraceSettings.symbolicate) { case Symbolication::Off: backtracer_argv[32] = "off"; break; case Symbolication::Fast: backtracer_argv[32] = "fast"; break; case Symbolication::Full: backtracer_argv[32] = "full"; break; } switch (_swift_backtraceSettings.format) { case OutputFormat::Text: backtracer_argv[34] = "text"; break; case OutputFormat::JSON: backtracer_argv[34] = "json"; break; } _swift_formatUnsigned(_swift_backtraceSettings.timeout, timeout_buf); if (_swift_backtraceSettings.limit < 0) std::strcpy(limit_buf, "none"); else _swift_formatUnsigned(_swift_backtraceSettings.limit, limit_buf); _swift_formatUnsigned(_swift_backtraceSettings.top, top_buf); _swift_formatAddress(crashInfo, addr_buf); // Set-up the environment array pid_t child; const char *env[BACKTRACE_MAX_ENV_VARS + 1]; const char *ptr = swiftBacktraceEnv; unsigned nEnv = 0; while (*ptr && nEnv < lengthof(env) - 1) { env[nEnv++] = ptr; ptr += std::strlen(ptr) + 1; }; env[nEnv] = 0; // SUSv3 says argv and envp are "completely constant" and that the reason // posix_spawn() et al use char * const * is for compatibility. #ifdef __linux__ int ret = safe_spawn(&child, swiftBacktracePath, memserver_fd, const_cast(backtracer_argv), const_cast(env)); #else int ret = posix_spawn(&child, swiftBacktracePath, nullptr, nullptr, const_cast(backtracer_argv), const_cast(env)); #endif if (ret < 0) return false; int wstatus; do { ret = waitpid(child, &wstatus, 0); } while (ret < 0 && errno == EINTR); if (WIFEXITED(wstatus)) return WEXITSTATUS(wstatus) == 0; return false; // ###TODO: Windows #endif } // N.B. THIS FUNCTION MUST BE SAFE TO USE FROM A CRASH HANDLER. On Linux // and macOS, that means it must be async-signal-safe. On Windows, there // isn't an equivalent notion but a similar restriction applies. SWIFT_RUNTIME_STDLIB_INTERNAL void _swift_displayCrashMessage(int signum, const void *pc) { #if !SWIFT_BACKTRACE_ON_CRASH_SUPPORTED return; #else int fd = STDERR_FILENO; if (_swift_backtraceSettings.outputTo == OutputTo::Stdout) fd = STDOUT_FILENO; const char *intro; if (_swift_backtraceSettings.color == OnOffTty::On) { intro = "\n💣 \033[91mProgram crashed: "; } else { intro = "\n*** "; } write(fd, intro, strlen(intro)); char sigbuf[30]; strcpy(sigbuf, "Signal "); _swift_formatUnsigned((unsigned)signum, sigbuf + 7); write(fd, sigbuf, strlen(sigbuf)); const char *message; if (!pc) { message = ": Backtracing"; } else { message = ": Backtracing from 0x"; } write(fd, message, strlen(message)); if (pc) { char pcbuf[18]; _swift_formatAddress(pc, pcbuf); write(fd, pcbuf, strlen(pcbuf)); } const char *outro; if (_swift_backtraceSettings.color == OnOffTty::On) { outro = "...\033[0m"; } else { outro = "..."; } write(fd, outro, strlen(outro)); #endif } } // namespace backtrace } // namespace runtime } // namespace swift