31 Commits

Author SHA1 Message Date
tomer doron 7f46ee0891 Update README.md 2021-07-23 10:29:29 -07:00
tomer doron 4ce28b1f31 Update README.md
Co-authored-by: Kaitlin Mahar <kaitlinmahar@gmail.com>
2021-07-21 18:04:54 -07:00
tomer doron 3e280f853c Update README.md 2021-07-21 17:46:36 -07:00
tomer doron e67fb71c9b Update README.md
motivation: highlight the background for this library and incourage use via service-lifecycle 

changes: update readme to reflect these ideas
2021-07-21 10:34:11 -07:00
Johannes Weiss d3e04a9d4b Merge pull request #49 from weissi/jw-other-platforms
forgot other platforms
2021-07-16 19:50:16 +01:00
Johannes Weiss b3b195e0e6 forgot other platforms 2021-07-16 19:34:47 +01:00
Johannes Weiss e434780428 Merge pull request #48 from weissi/jw-signals
allow other signals
2021-07-15 21:24:11 +01:00
Johannes Weiss ceaaf36f0f allow other signals 2021-07-15 19:40:14 +01:00
Konrad `ktoso` Malawski bb3bd2357e Merge pull request #45 from swift-server/tomerd-patch-1
adopt SSWG security guidelines
2021-06-21 11:24:12 +09:00
tomer doron 5f7686a18b Merge branch 'main' into tomerd-patch-1 2021-06-11 09:13:34 -07:00
tomer doron d9655c7867 add 5.4 CI (#46)
motivation: 5.4 was released, add 5.4 CI

changes: add docker-compose setup for 5.4
2021-06-11 09:13:24 -07:00
tomer doron 84bcaad52f Update README.md 2021-06-08 10:38:03 -07:00
tomer doron 7152ac0033 adopt SSWG security guidelines
add SECURITY.md detailing the security process
2021-06-08 10:37:16 -07:00
tomer doron 54a65d6391 recent changes to support windows broke macos (#44)
motivation: unbreak macos

changes: make _stdlib_demangleName only available on macOS and Windows where it is fully supported
2021-04-20 11:04:11 -07:00
Saleem Abdulrasool 1ca1dbd9ff README: add acknowledgement for Windows port (#43) 2021-04-20 10:10:33 -07:00
Johannes Weiss 182caea1a4 Merge pull request #42 from compnerd/windows
Backtrace: enable support for Windows
2021-04-20 14:04:13 +01:00
Saleem Abdulrasool 5b47490c74 Backtrace: adjust style (NFC)
Adjust the style for the linter.
2021-04-19 13:13:09 -07:00
Saleem Abdulrasool b097912943 Backtrace: enable support for Windows
Add an implementation across all architectures supported by Windows:
- x86
- x86_64
- ARM
- ARM64

This relies on DbgHlp32.dll which makes this unfriendly to store
applications.  We can investigate improvements to that in the future.
This sketches out an implementation which allows building and using the
interface for providing backtraces.  This requires that the programs be
built with debug information in CodeView format.  Without the additional
debug information, the stack traces will be limited to symbolication and
resolution to export only symbols.

Resolves: #34
2021-04-18 11:16:45 -07:00
Saleem Abdulrasool a4f7b4204e Backtrace: make _stdlib_demangleName part of all platforms (#41) 2021-04-17 21:45:37 -07:00
Saleem Abdulrasool 9a2ef536ee git: ignore vim swap files (#39) 2021-04-17 19:54:24 -07:00
Johannes Weiss c859218177 Merge pull request #37 from mpdifran/patch-1
Update build flags with Swift version info
2021-03-03 10:24:02 +00:00
Mark DiFranco 1d5fbd8c42 Update build flags with Swift version info
As of Swift 5.2, this build flag is no longer necessary.
2021-03-01 09:11:11 -05:00
Johannes Weiss 93b3d9a764 Merge pull request #35 from weissi/jw-error
better distinguish mmap errors & print errno
2021-02-22 11:10:40 +00:00
Johannes Weiss 4353fd38d5 Merge branch 'main' into jw-error 2021-02-22 11:09:50 +00:00
Johannes Weiss 97a034c8a4 Merge pull request #36 from weissi/jw-exe-name
determine the executable path automatically
2021-02-22 11:09:38 +00:00
Johannes Weiss 566d2454b2 determine the executable path automatically
Motivation:

Previously, we would use
`CommandLine.arguments[0]`/`argv[0]`/`getexecname()` as the preferred
ways to find the main binary. That sounds sensible, especially given
the fact that we'd fall back onto other methods (such as
`/proc/self/exe`) when the previous ones don't work.

There is however an unfortunate edge case. If you for example have your
app binary at `/app/app` and you start it with a relative path `./app`
from within `/app`, then `CommandLine.arguments[0]` will only contain
`./app`. If now the app changes its working directory to `/`, `./app`
would mean `/app` which is the directory. This would mean we will find
the target but instead of the binary we'd find the directory `/app`.
From then on, everything else fails.

A similar problem is if `argv[0]` was actually set incorrectly when
`exec*`ing the process. This has happened at least in one instance.

Modification:

Given that we only target Linux here, we can actually here, we can
change the order in which we try to find the main binary. We can start
by using `/proc/self/exe` which is a very high quality and almost
guaranteed to work way to find the main binary, nevermind the
information in `argv`.

Result:

More robust symbolications, especially in environments like Heroku/Dokku
where we've seen `argv` not being set correctly.
2021-02-21 18:28:38 +00:00
Johannes Weiss c8d8ddd88b better distinguish mmap errors & print errno
Motivation:

In case SwiftBacktrace fails, we get only very little output.
Unfortunately, some crucial information (like the errno) is missing and
in case `mmap` fails, we don't know if the problem is file I/O or
allocating memory.

Modification:

- print errnos
- distinguish the two mmaps

Result:

Easier debugging
2021-02-20 23:12:33 +00:00
tomer doron 4915cdd24e remove symbolicate-linux-fatal from Docker (#33)
motivation: we are not actually using symbolicate-linux-fatal in any meaningful way in CI and it's pinned to the master branch which has been removed

changes: remove symbolicate-linux-fatal fetching from Docker
2020-10-07 17:29:02 -07:00
tomer doron 7a48ec7baa Swift 5.3 was released, use if for CI (#32)
motivation: Swift 5.3 was released, use if for CI

changes: update docker setup to use the release version of 5.3
2020-09-18 12:59:21 -07:00
tomer doron c6559c866c stabilize ci (#31)
motivation: update swift-server ci

changes: 
* remove travis ci definition
* dont install jazzy on 16.04 - its broken on that version and not in use  (stabilize ci)
* using 5.3 nightlies (stabilize ci)
2020-08-19 11:33:07 -07:00
tomer doron 232bd5bdbc update ci setup (#27)
motivation: 5.2 adoption, prepare for 5.3

changes:
* add 5.2 docker-compose setup
* add 5.3 docker-compose setup (placeholder)
2020-04-04 00:20:09 -07:00
14 changed files with 292 additions and 72 deletions
+1
View File
@@ -3,3 +3,4 @@
/Packages
/*.xcodeproj
.swiftpm
.*.sw[nop]
-1
View File
@@ -8,7 +8,6 @@
--self insert
--patternlet inline
--stripunusedargs unnamed-only
--comments ignore
--ifdef no-indent
# rules
-50
View File
@@ -1,50 +0,0 @@
branches:
only:
- master
# The matrix of builds should cover each combination of Swift version
# and platform that is supported. The version of Swift used is specified
# by .swift-version, unless SWIFT_SNAPSHOT is specified.
matrix:
include:
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE=swift:4.2.4 SWIFT_SNAPSHOT=4.2.4
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE=swift:5.0-xenial
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE=swift:5.0-bionic
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE=swift:5.1-xenial
- os: linux
dist: xenial
sudo: required
services: docker
env: DOCKER_IMAGE=swift:5.1-bionic
- os: osx
osx_image: xcode10.1
sudo: required
env: SWIFT_SNAPSHOT=4.2.1
- os: osx
osx_image: xcode10.3
sudo: required
- os: osx
osx_image: xcode11.2
sudo: required
before_install:
- git clone https://github.com/IBM-Swift/Package-Builder.git
script:
- ./Package-Builder/build-package.sh -projectDir $TRAVIS_BUILD_DIR
+12 -1
View File
@@ -2,8 +2,14 @@
This Swift package provides support for automatically printing crash backtraces of Swift programs.
The library is designed to fill a gap in backtraces support for Swift on non-Darwin platforms.
When this gap is closed at the language runtime level, this library will become redundant and be deprecated.
## Usage
When building web-services and daemons, direct usage of this library is discouraged.
Instead, use [swift-service-lifecycle](https://github.com/swift-server/swift-service-lifecycle) which helps manage the application lifecycle including setting up backtraces hooks when needed.
Add `https://github.com/swift-server/swift-backtrace.git` as a dependency in your `Package.swift`.
### Crash backtraces
@@ -17,7 +23,7 @@ import Backtrace
Backtrace.install()
```
Finally, make sure you build your application with debug symbols enabled:
Finally, for Swift < 5.2, make sure you build your application with debug symbols enabled. Debug symbols are automatically included for Swift 5.2 and above.
```
$ swift build -c release -Xswiftc -g
@@ -25,9 +31,14 @@ $ swift build -c release -Xswiftc -g
When your app crashes, a stacktrace will be printed to `stderr`.
## Security
Please see [SECURITY.md](SECURITY.md) for details on the security process.
## Acknowledgements
Ian Partridge ([GitHub](https://github.com/ianpartridge/), [Twitter](https://twitter.com/alfa)) the original author of this package.
Johannes Weiss ([GitHub](https://github.com/weissi), [Twitter](https://twitter.com/johannesweiss)) for the signal handling code.
Saleem Abdulrasool ([GitHub](https://github.com/compnerd), [Twitter](https://twitter.com/compnerd)) for the Windows port.
+43
View File
@@ -0,0 +1,43 @@
# Security
This document specifies the security process for the Backtrace project.
## Disclosures
### Private Disclosure Process
The Backtrace maintainers ask that known and suspected vulnerabilities be
privately and responsibly disclosed by emailing
[sswg-security-reports@forums.swift.org](mailto:sswg-security-reports@forums.swift.org)
with the all the required detail.
**Do not file a public issue.**
#### When to report a vulnerability
* You think you have discovered a potential security vulnerability in Backtrace.
* You are unsure how a vulnerability affects Backtrace.
#### What happens next?
* A member of the team will acknowledge receipt of the report within 3
working days (United States). This may include a request for additional
information about reproducing the vulnerability.
* We will privately inform the Swift Server Work Group ([SSWG][sswg]) of the
vulnerability within 10 days of the report as per their [security
guidelines][sswg-security].
* Once we have identified a fix we may ask you to validate it. We aim to do this
within 30 days. In some cases this may not be possible, for example when the
vulnerability exists at the protocol level and the industry must coordinate on
the disclosure process.
* If a CVE number is required, one will be requested from [MITRE][mitre]
providing you with full credit for the discovery.
* We will decide on a planned release date and let you know when it is.
* Prior to release, we will inform major dependents that a security-related
patch is impending.
* Once the fix has been released we will publish a security advisory on GitHub
and in the Server → Security Updates category on the [Swift forums][swift-forums-sec].
[sswg]: https://github.com/swift-server/sswg
[sswg-security]: https://github.com/swift-server/sswg/blob/main/security/README.md
[swift-forums-sec]: https://forums.swift.org/c/server/security-updates/
[mitre]: https://cveform.mitre.org/
+182 -6
View File
@@ -21,7 +21,7 @@ typealias CBacktraceFullCallback = @convention(c) (_ data: UnsafeMutableRawPoint
typealias CBacktraceSimpleCallback = @convention(c) (_ data: UnsafeMutableRawPointer?, _ pc: UInt) -> CInt
typealias CBacktraceSyminfoCallback = @convention(c) (_ data: UnsafeMutableRawPointer?, _ pc: UInt, _ filename: UnsafePointer<CChar>?, _ symval: UInt, _ symsize: UInt) -> Void
private let state = backtrace_create_state(CommandLine.arguments[0], /* BACKTRACE_SUPPORTS_THREADS */ 1, nil, nil)
private let state = backtrace_create_state(nil, /* BACKTRACE_SUPPORTS_THREADS */ 1, nil, nil)
private let fullCallback: CBacktraceFullCallback? = {
_, pc, filename, lineno, function in
@@ -53,18 +53,31 @@ private let fullCallback: CBacktraceFullCallback? = {
}
private let errorCallback: CBacktraceErrorCallback? = {
_, msg, _ in
_, msg, errNo in
if let msg = msg {
_ = withVaList([msg]) { vaList in
vfprintf(stderr, "%s\n", vaList)
_ = withVaList([msg, errNo]) { vaList in
vfprintf(stderr, "SwiftBacktrace ERROR: %s (errno: %d)\n", vaList)
}
}
}
private func printBacktrace(signal: CInt) {
_ = fputs("Received signal \(signal). Backtrace:\n", stderr)
backtrace_full(state, /* skip */ 0, fullCallback, errorCallback, nil)
}
public enum Backtrace {
/// Install the backtrace handler on `SIGILL`.
public static func install() {
self.setupHandler(signal: SIGILL) { _ in
backtrace_full(state, /* skip */ 0, fullCallback, errorCallback, nil)
Backtrace.install(signals: [SIGILL])
}
/// Install the backtrace handler when any of `signals` happen.
public static func install(signals: [CInt]) {
for signal in signals {
self.setupHandler(signal: signal) { signal in
printBacktrace(signal: signal)
}
}
}
@@ -86,10 +99,173 @@ public enum Backtrace {
}
}
#elseif os(Windows)
#if swift(<5.4)
#error("unsupported Swift version")
#else
@_implementationOnly import CRT
@_implementationOnly import WinSDK
#endif
public enum Backtrace {
private static var MachineType: DWORD {
#if arch(arm)
DWORD(IMAGE_FILE_MACHINE_ARMNT)
#elseif arch(arm64)
DWORD(IMAGE_FILE_MACHINE_ARM64)
#elseif arch(i386)
DWORD(IMAGE_FILE_MACHINE_I386)
#elseif arch(x86_64)
DWORD(IMAGE_FILE_MACHINE_AMD64)
#else
#error("unsupported architecture")
#endif
}
@available(*, deprecated, message: "signal selection unavailable on Windows")
public static func install(signals: [CInt]) {
Backtrace.install()
}
public static func install() {
// Install a last-chance vectored exception handler to capture the error
// before the termination and report the stack trace. It is unlikely
// that this will be recovered at this point by a SEH handler.
_ = AddVectoredExceptionHandler(0) { _ in
// NOTE: GetCurrentProcess does not increment the reference count on
// the process. This handle should _not_ be closed upon completion.
let hProcess: HANDLE = GetCurrentProcess()
var cxr: CONTEXT = CONTEXT()
cxr.ContextFlags =
DWORD(CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_FLOATING_POINT)
RtlCaptureContext(&cxr)
_ = SymInitializeW(hProcess, nil, true)
_ = SymSetOptions(DWORD(SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES | SYMOPT_UNDNAME))
var Frame: STACKFRAME64 = STACKFRAME64()
#if arch(arm)
Frame.AddrPC.Offset = cxr.Pc
Frame.AddrFrame.Offset = cxr.R11
Frame.AddrStack.Offset = cxr.Sp
#elseif arch(arm64)
Frame.AddrPC.Offset = cxr.Pc
Frame.AddrFrame.Offset = cxr.Fp
Frame.AddrStack.Offset = cxr.Sp
#elseif arch(i386)
Frame.AddrPC.Offset = cxr.Eip
Frame.AddrFrame.Offset = cxr.Ebp
Frame.AddrStack.Offset = cxr.Esp
#elseif arch(x86_64)
Frame.AddrPC.Offset = cxr.Rip
Frame.AddrFrame.Offset = cxr.Rbp
Frame.AddrStack.Offset = cxr.Rsp
#else
#error("unsupported architecture")
#endif
Frame.AddrPC.Mode = AddrModeFlat
Frame.AddrFrame.Mode = AddrModeFlat
Frame.AddrStack.Mode = AddrModeFlat
// Constant indicating the maximum symbol length that we expect
// during symbolication of the stack trace.
let kMaxSymbolLength: Int = 255
// Heap allocate the buffer as we need to account for the trailing
// storage that we need to provide.
let pSymbolBuffer: UnsafeMutableRawPointer =
UnsafeMutableRawPointer.allocate(byteCount: MemoryLayout<IMAGEHLP_SYMBOL64>.size + kMaxSymbolLength,
alignment: 1)
defer { pSymbolBuffer.deallocate() }
let pSymbol: UnsafeMutablePointer<IMAGEHLP_SYMBOL64> =
pSymbolBuffer.bindMemory(to: IMAGEHLP_SYMBOL64.self,
capacity: 1)
let hThread: HANDLE = GetCurrentThread()
while StackWalk64(Backtrace.MachineType, hProcess, hThread,
&Frame, &cxr, nil, SymFunctionTableAccess64,
SymGetModuleBase64, nil) {
var qwModuleBase: DWORD64 =
SymGetModuleBase64(hProcess, Frame.AddrPC.Offset)
let module: String = withUnsafeMutablePointer(to: &qwModuleBase) {
$0.withMemoryRebound(to: HINSTANCE.self, capacity: 1) { hInstance in
String(decoding: [WCHAR](unsafeUninitializedCapacity: Int(MAX_PATH + 1)) {
$1 = Int(GetModuleFileNameW(hInstance.pointee,
$0.baseAddress,
DWORD($0.count)))
}, as: UTF16.self)
}
}
pSymbol.pointee.SizeOfStruct =
DWORD(MemoryLayout<IMAGEHLP_SYMBOL64>.size)
pSymbol.pointee.MaxNameLength = DWORD(kMaxSymbolLength)
_ = SymGetSymFromAddr64(hProcess, Frame.AddrPC.Offset, nil,
pSymbol)
var symbol: String =
withUnsafePointer(to: &pSymbol.pointee.Name) {
String(cString: $0)
}
// Undecorate Swift 3+ names only. Earlier Swift decorations
// are unsupported. Any MSVC name decoration has been
// unperformed during the DbgHelp operation through the use of
// the `SYMOPT_UNDNAME` option.
if symbol.hasPrefix("$s") || symbol.hasPrefix("$S") {
symbol = _stdlib_demangleName(symbol)
}
var Displacement: DWORD = 0
var Line: IMAGEHLP_LINE64 = IMAGEHLP_LINE64()
Line.SizeOfStruct = DWORD(MemoryLayout<IMAGEHLP_LINE64>.size)
_ = SymGetLineFromAddr64(hProcess, Frame.AddrPC.Offset,
&Displacement, &Line)
var details: String = ""
if !symbol.isEmpty {
// Truncate the module path to the filename. The
// `PathFindFileNameW` call will return the beginning of the
// string if a path separator character is not found.
if let pszModule = module.withCString(encodedAs: UTF16.self,
PathFindFileNameW) {
details.append(", \(String(decodingCString: pszModule, as: UTF16.self))!\(symbol)")
}
}
if let szFileName = Line.FileName {
details.append(" at \(String(cString: szFileName)):\(Line.LineNumber)")
}
_ = details.withCString { pszDetails in
withVaList([Frame.AddrPC.Offset, pszDetails]) {
#if arch(arm64) || arch(x86_64)
vfprintf(stderr, "%#016x%s\n", $0)
#else
vfprintf(stderr, "%#08x%s\n", $0)
#endif
}
}
}
_ = SymCleanup(hProcess)
// We have not handled the exception, continue the search.
return EXCEPTION_CONTINUE_SEARCH
}
}
}
#else
public enum Backtrace {
public static func install() {}
public static func install(signals: [CInt]) {}
@available(*, deprecated, message: "This method will be removed in the next major version.")
public static func print() {}
}
+9
View File
@@ -14,7 +14,16 @@
#if os(Linux)
import Glibc
#elseif os(Windows)
#if swift(<5.4)
#error("unsupported Swift version")
#else
@_implementationOnly
import ucrt
#endif
#endif
#if os(Linux) || os(Windows)
@_silgen_name("swift_demangle")
public
func _stdlib_demangleImpl(
+4 -4
View File
@@ -94,14 +94,14 @@ fileline_initialize (struct backtrace_state *state,
filename = state->filename;
break;
case 1:
filename = getexecname ();
break;
case 2:
filename = "/proc/self/exe";
break;
case 3:
case 2:
filename = "/proc/curproc/file";
break;
case 3:
filename = getexecname ();
break;
case 4:
snprintf (buf, sizeof (buf), "/proc/%ld/object/a.out",
(long) getpid ());
+1 -1
View File
@@ -170,7 +170,7 @@ backtrace_alloc (struct backtrace_state *state,
if (page == MAP_FAILED)
{
if (error_callback)
error_callback (data, "mmap", errno);
error_callback (data, "mmap for alloc", errno);
}
else
{
+1 -1
View File
@@ -71,7 +71,7 @@ backtrace_get_view (struct backtrace_state *state ATTRIBUTE_UNUSED,
map = mmap (NULL, size, PROT_READ, MAP_PRIVATE, descriptor, pageoff);
if (map == MAP_FAILED)
{
error_callback (data, "mmap", errno);
error_callback (data, "mmap file i/o", errno);
return 0;
}
+5 -7
View File
@@ -17,20 +17,18 @@ RUN apt-get update && apt-get install -y wget
RUN apt-get update && apt-get install -y lsof dnsutils netcat-openbsd net-tools curl jq # used by integration tests
# ruby and jazzy for docs generation
RUN apt-get update && apt-get install -y ruby ruby-dev libsqlite3-dev
RUN gem install jazzy --no-ri --no-rdoc
RUN apt-get update && apt-get install -y ruby ruby-dev libsqlite3-dev build-essential
# switch of gem docs building
RUN echo "gem: --no-document" > ~/.gemrc
RUN if [ "${ubuntu_version}" != "xenial" ] ; then gem install jazzy ; fi
# tools
RUN mkdir -p $HOME/.tools
RUN echo 'export PATH="$HOME/.tools:$PATH"' >> $HOME/.profile
# script to allow mapping framepointers on linux (until part of the toolchain)
RUN wget -q https://raw.githubusercontent.com/apple/swift/master/utils/symbolicate-linux-fatal -O $HOME/.tools/symbolicate-linux-fatal
RUN chmod 755 $HOME/.tools/symbolicate-linux-fatal
# swiftformat (until part of the toolchain)
ARG swiftformat_version=0.44.0
ARG swiftformat_version=0.44.6
RUN git clone --branch $swiftformat_version --depth 1 https://github.com/nicklockwood/SwiftFormat $HOME/.tools/swift-format
RUN cd $HOME/.tools/swift-format && swift build -c release
RUN ln -s $HOME/.tools/swift-format/.build/release/swiftformat $HOME/.tools/swiftformat
+2 -1
View File
@@ -6,7 +6,8 @@ services:
image: swift-linux-backtrace:18.04-5.2
build:
args:
base_image: "swiftlang/swift:nightly-5.2-bionic"
ubuntu_version: "bionic"
swift_version: "5.2"
test:
image: swift-linux-backtrace:18.04-5.2
+16
View File
@@ -0,0 +1,16 @@
version: "3"
services:
runtime-setup:
image: swift-linux-backtrace:18.04-5.3
build:
args:
ubuntu_version: "bionic"
swift_version: "5.3"
test:
image: swift-linux-backtrace:18.04-5.3
shell:
image: swift-linux-backtrace:18.04-5.3
+16
View File
@@ -0,0 +1,16 @@
version: "3"
services:
runtime-setup:
image: swift-linux-backtrace:20.04-5.4
build:
args:
ubuntu_version: "focal"
swift_version: "5.4"
test:
image: swift-linux-backtrace:20.04-5.4
shell:
image: swift-linux-backtrace:20.04-5.4