You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Captures the callstack directly in the panic hook (using the backtrace crate) and stores it alongside the panic message. When the signal handler fires after a panic, the pre-captured callstack is used instead of unwinding from the signal handler's ucontext. A new CrashKindData::Panic variant distinguishes panic-originated crashes from signal-based crashes and unhandled exceptions, and the error kind is set to ErrorKind::Panic accordingly.
Key changes:
Added PANIC_CALLSTACK atomic global to store the callstack captured in the panic hook
Introduced capture_panic_callstack() which uses backtrace::trace_unsynchronized to walk the stack and collect ip, sp, symbol address, and module base address per frame
Refactored CrashKindData to add a Panic variant carrying both the stacktrace and message, and moved message_ptr into the per-variant data where it belongs
Simplified Collector::spawn signature by passing CrashKindData directly instead of individual raw pointers
The signal handler now checks if a panic message was stored: if so, it builds a CrashKindData::Panic; otherwise it falls back to CrashKindData::UnixSignal
Motivation
Previously, for panics compiled with panic = "abort", the crash report callstack was captured from the signal handler after SIGABRT was raised. This had several problems:
Polluted stack: the abort machinery frames (__GI_raise, rust_panic_abort, etc.) appear on top of the actual panic site
Unreliable on unwind mode: if panic = "unwind" is used and the panic crosses an FFI boundary, the stack may be partially or fully destroyed by the time the signal handler fires
Broken on Alpine/musl: musl's signal trampoline lacks DWARF unwind info (missing FDE), making signal-handler-based unwinding fragile. While the existing libunwind-seeded-from-ucontext approach mitigates this for signal crashes, capturing in the panic hook avoids the problem entirely since there is no signal trampoline to cross
Signal handler may never fire: in unwind mode, if a catch_unwind boundary exists or the thread simply terminates, no signal is raised and the callstack is lost entirely
Capturing in the panic hook gives us the cleanest possible callstack: the stack is fully intact, we're in normal Rust context (safe to allocate, use libunwind, etc.), and it works identically in both unwind and abort modes.
Additional Notes
The backtrace crate is added as a new dependency. It uses trace_unsynchronized to avoid locking overhead since the panic hook is single-threaded per panicking thread.
The message_ptr field was moved from being a separate parameter in emit_crashreport into the CrashKindData variants (Panic and UnhandledException), since UnixSignal crashes don't carry a panic message. This makes the data flow more explicit.
Symbol resolution is not performed in the panic hook — only raw addresses (ip, sp, symbol_address, module_base_address) are captured. The receiver's blazesym pass handles symbolication as before.
How to test the change?
Run the existing crash tracker binary tests: cargo test -p bin_tests -- crashtracker
Verify that the "panic" crash type test now asserts error.kind == "Panic"
Unit tests for PANIC_CALLSTACK atomic storage/retrieval/replacement are included
Test on Alpine (musl) to confirm that panic callstacks are captured correctly without depending on signal-handler unwinding
⚠️4 issue(s) found, showing only errors (advisories, bans, sources)
📦 libdd-crashtracker - 4 error(s)
Show output
error[unsound]: Rand is unsound with a custom logger using `rand::rng()`
┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:148:1
│
148 │ rand 0.8.5 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ unsound advisory detected
│
├ ID: RUSTSEC-2026-0097
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0097
├ It has been reported (by @lopopolo) that the `rand` library is [unsound](https://rust-lang.github.io/unsafe-code-guidelines/glossary.html#soundness-of-code--of-a-library) (i.e. that safe code using the public API can cause Undefined Behaviour) when all the following conditions are met:
- The `log` and `thread_rng` features are enabled
- A [custom logger](https://docs.rs/log/latest/log/#implementing-a-logger) is defined
- The custom logger accesses `rand::rng()` (previously `rand::thread_rng()`) and calls any `TryRng` (previously `RngCore`) methods on `ThreadRng`
- The `ThreadRng` (attempts to) reseed while called from the custom logger (this happens every 64 kB of generated data)
- Trace-level logging is enabled or warn-level logging is enabled and the random source (the `getrandom` crate) is unable to provide a new seed
`TryRng` (previously `RngCore`) methods for `ThreadRng` use `unsafe` code to cast `*mut BlockRng<ReseedingCore>` to `&mut BlockRng<ReseedingCore>`. When all the above conditions are met this results in an aliased mutable reference, violating the Stacked Borrows rules. Miri is able to detect this violation in sample code. Since construction of [aliased mutable references is Undefined Behaviour](https://doc.rust-lang.org/stable/nomicon/references.html), the behaviour of optimized builds is hard to predict.
├ Announcement: https://github.com/rust-random/rand/pull/1763
├ Solution: Upgrade to >=0.10.1 OR <0.10.0, >=0.9.3 OR <0.9.0, >=0.8.6 (try `cargo update -p rand`)
├ rand v0.8.5
├── libdd-common v3.0.2
│ ├── (build) libdd-crashtracker v1.0.0
│ ├── libdd-shared-runtime v0.1.0
│ │ └── libdd-telemetry v4.0.0
│ │ └── libdd-crashtracker v1.0.0 (*)
│ └── libdd-telemetry v4.0.0 (*)
└── libdd-crashtracker v1.0.0 (*)
error[vulnerability]: Name constraints for URI names were incorrectly accepted
┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:162:1
│
162 │ rustls-webpki 0.103.10 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
│
├ ID: RUSTSEC-2026-0098
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0098
├ Name constraints for URI names were ignored and therefore accepted.
Note this library does not provide an API for asserting URI names, and URI name constraints are otherwise not implemented. URI name constraints are now rejected unconditionally.
Since name constraints are restrictions on otherwise properly-issued certificates, this bug is reachable only after signature verification and requires misissuance to exploit.
This vulnerability is identified as [GHSA-965h-392x-2mh5](https://github.com/rustls/webpki/security/advisories/GHSA-965h-392x-2mh5). Thank you to @1seal for the report.
├ Solution: Upgrade to >=0.103.12, <0.104.0-alpha.1 OR >=0.104.0-alpha.6 (try `cargo update -p rustls-webpki`)
├ rustls-webpki v0.103.10
└── rustls v0.23.37
├── hyper-rustls v0.27.7
│ └── libdd-common v3.0.2
│ ├── (build) libdd-crashtracker v1.0.0
│ ├── libdd-shared-runtime v0.1.0
│ │ └── libdd-telemetry v4.0.0
│ │ └── libdd-crashtracker v1.0.0 (*)
│ └── libdd-telemetry v4.0.0 (*)
├── libdd-common v3.0.2 (*)
└── tokio-rustls v0.26.0
├── hyper-rustls v0.27.7 (*)
└── libdd-common v3.0.2 (*)
error[vulnerability]: Name constraints were accepted for certificates asserting a wildcard name
┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:162:1
│
162 │ rustls-webpki 0.103.10 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
│
├ ID: RUSTSEC-2026-0099
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0099
├ Permitted subtree name constraints for DNS names were accepted for certificates asserting a wildcard name.
This was incorrect because, given a name constraint of `accept.example.com`, `*.example.com` could feasibly allow a name of `reject.example.com` which is outside the constraint.
This is very similar to [CVE-2025-61727](https://go.dev/issue/76442).
Since name constraints are restrictions on otherwise properly-issued certificates, this bug is reachable only after signature verification and requires misissuance to exploit.
This vulnerability is identified as [GHSA-xgp8-3hg3-c2mh](https://github.com/rustls/webpki/security/advisories/GHSA-xgp8-3hg3-c2mh). Thank you to @1seal for the report.
├ Solution: Upgrade to >=0.103.12, <0.104.0-alpha.1 OR >=0.104.0-alpha.6 (try `cargo update -p rustls-webpki`)
├ rustls-webpki v0.103.10
└── rustls v0.23.37
├── hyper-rustls v0.27.7
│ └── libdd-common v3.0.2
│ ├── (build) libdd-crashtracker v1.0.0
│ ├── libdd-shared-runtime v0.1.0
│ │ └── libdd-telemetry v4.0.0
│ │ └── libdd-crashtracker v1.0.0 (*)
│ └── libdd-telemetry v4.0.0 (*)
├── libdd-common v3.0.2 (*)
└── tokio-rustls v0.26.0
├── hyper-rustls v0.27.7 (*)
└── libdd-common v3.0.2 (*)
error[vulnerability]: Reachable panic in certificate revocation list parsing
┌─ /home/runner/work/libdatadog/libdatadog/Cargo.lock:162:1
│
162 │ rustls-webpki 0.103.10 registry+https://github.com/rust-lang/crates.io-index
│ ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ security vulnerability detected
│
├ ID: RUSTSEC-2026-0104
├ Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0104
├ A panic was reachable when parsing certificate revocation lists via [`BorrowedCertRevocationList::from_der`]
or [`OwnedCertRevocationList::from_der`]. This was the result of mishandling a syntactically valid empty
`BIT STRING` appearing in the `onlySomeReasons` element of a `IssuingDistributionPoint` CRL extension.
This panic is reachable prior to a CRL's signature being verified.
Applications that do not use CRLs are not affected.
Thank you to @tynus3 for the report.
├ Solution: Upgrade to >=0.103.13, <0.104.0-alpha.1 OR >=0.104.0-alpha.7 (try `cargo update -p rustls-webpki`)
├ rustls-webpki v0.103.10
└── rustls v0.23.37
├── hyper-rustls v0.27.7
│ └── libdd-common v3.0.2
│ ├── (build) libdd-crashtracker v1.0.0
│ ├── libdd-shared-runtime v0.1.0
│ │ └── libdd-telemetry v4.0.0
│ │ └── libdd-crashtracker v1.0.0 (*)
│ └── libdd-telemetry v4.0.0 (*)
├── libdd-common v3.0.2 (*)
└── tokio-rustls v0.26.0
├── hyper-rustls v0.27.7 (*)
└── libdd-common v3.0.2 (*)
advisories FAILED, bans ok, sources ok
This report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality.
❌ Patch coverage is 44.14414% with 62 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.75%. Comparing base (cf8c1cf) to head (d73cb3a).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Captures the callstack directly in the panic hook (using the
backtracecrate) and stores it alongside the panic message. When the signal handler fires after a panic, the pre-captured callstack is used instead of unwinding from the signal handler'sucontext. A newCrashKindData::Panicvariant distinguishes panic-originated crashes from signal-based crashes and unhandled exceptions, and the error kind is set toErrorKind::Panicaccordingly.Key changes:
PANIC_CALLSTACKatomic global to store the callstack captured in the panic hookcapture_panic_callstack()which usesbacktrace::trace_unsynchronizedto walk the stack and collect ip, sp, symbol address, and module base address per frameCrashKindDatato add aPanicvariant carrying both the stacktrace and message, and movedmessage_ptrinto the per-variant data where it belongsCollector::spawnsignature by passingCrashKindDatadirectly instead of individual raw pointersCrashKindData::Panic; otherwise it falls back toCrashKindData::UnixSignalMotivation
Previously, for panics compiled with
panic = "abort", the crash report callstack was captured from the signal handler afterSIGABRTwas raised. This had several problems:__GI_raise,rust_panic_abort, etc.) appear on top of the actual panic sitepanic = "unwind"is used and the panic crosses an FFI boundary, the stack may be partially or fully destroyed by the time the signal handler firescatch_unwindboundary exists or the thread simply terminates, no signal is raised and the callstack is lost entirelyCapturing in the panic hook gives us the cleanest possible callstack: the stack is fully intact, we're in normal Rust context (safe to allocate, use libunwind, etc.), and it works identically in both
unwindandabortmodes.Additional Notes
backtracecrate is added as a new dependency. It usestrace_unsynchronizedto avoid locking overhead since the panic hook is single-threaded per panicking thread.message_ptrfield was moved from being a separate parameter inemit_crashreportinto theCrashKindDatavariants (PanicandUnhandledException), sinceUnixSignalcrashes don't carry a panic message. This makes the data flow more explicit.How to test the change?
cargo test -p bin_tests -- crashtracker"panic"crash type test now assertserror.kind == "Panic"PANIC_CALLSTACKatomic storage/retrieval/replacement are included