Skip to content

Conversation

@supervacuus
Copy link
Collaborator

@supervacuus supervacuus commented Nov 10, 2025

This is a more elaborate and long-term fix to getsentry/sentry-java#4830 compared to #1444.

It also finishes the work done here: #1088
And fixes the issues raised here: #1353
and here: #906

So, while the driver for this PR is a downstream issue that exposes the signal-unsafety of some parts in the current inproc implementation with an actual bug, it addresses a much broader range of concerns that regularly affect inproc users on all platforms.

On a high level, it introduces a separate handler thread for inproc, which the signal-handler (or UEF on Windows) wakes after it exchanges crash context data.

The idea is that we minimize signal handler/UEF to do the least amount of syscall stuff (or at least the subset documented in the signal-safety man-page, while the handler thread can execute functions outside that range (with limitations, since thread sync and heap allocations are still problematic). This allows us to reuse stdio functionality like formatters, without running squarely into UB territory or having to rewrite all utilities to async-signal safe versions like in #1444.

There are a few considerable changes to mention:

  • since we run the event construction in a separate handler thread, the use of backtrace() or any unwinder that runs from the "current" instruction address is entirely useless (ignoring the fact that backtrace() was always signal-unsafe to begin with which itself was the source of crashes, hangs or just empty stack traces).
  • this means we require a "user context"-based stack walker in inproc, which we already partially acknowledged in Using libunwind for mac, since backtrace do not expect thread context… #1088 and fix: support musl on Linux #1233.
  • on Linux, this PR requires libunwind (the nognu implementation, not the llvm one, which is a pure C++ exception unwinder), which is a breaking change (at least in the sense that users now require an additional dependency at build and runtime). This means that the "general" Linux usage is now the same as with the musl libc environments.
  • on macOS, we provide a user context stack-walker based on frame pointer records for arm64 and x86-64, and use the system-provided libunwind for the default stack-trace from a call-site. It turned out that the system-provided libunwind wasn't safe enough to use in the context of the signal handler (either led to hangs or had issues with escaping the trampoline). This means users can now use inproc on macOS again (if their code is compiled without omitting frame-pointers, which by default is always the case on macOS).

open TODO:

  • Decide on having the change
  • provide a module-level description of the new mechanism in inproc
  • finish the x86-64 stackwalker for macOS (and clean up the code)
  • Update documentation
    • Advanced usage might be outdated wrt to signal handling of inproc
    • Remove mentions of inproc not working on macOS
    • Clarify the new libunwind dependency on Linux
  • Figure out if we need the libbacktrace fallback at all and how to handle it.

#skip-changelog (for now)

* use `std::nothrow` `new` consistently to keep exception-free semantics for allocation
* rename static crashpad_handler to have no module-public prefix
* use `nullptr` for arguments where we previously used 0 to clarify that those are pointers
* eliminate the `memset()` of the `crashpad_state_t` initialization since it now contains non-trivially constructable fields (`std::atomic`) and replace it with `new` and an empty value initializer.
…ld, since libraries like libunwind.a might be packaged without PIC.
…ms with architecture prefixes (32-bit Linux)
…stack

also ensure to get the first frame
harmonize libunwind usage
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants