Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

**Fixes**:

- Fix `CHAIN_AT_START` handler strategy crashing when the chained handler resets the signal handler and re-raises. ([#1572](https://github.com/getsentry/sentry-native/pull/1572))

## 0.13.2

**Features**:
Expand Down
23 changes: 23 additions & 0 deletions src/backends/sentry_backend_inproc.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include <limits.h>
#ifdef SENTRY_PLATFORM_UNIX
# include <poll.h>
# include <sys/syscall.h>
#endif
#include <string.h>

Expand Down Expand Up @@ -1563,6 +1564,14 @@ process_ucontext(const sentry_ucontext_t *uctx)
uintptr_t ip = get_instruction_pointer(uctx);
uintptr_t sp = get_stack_pointer(uctx);

// Mask the signal so SA_NODEFER doesn't let re-raises from the chained
// handler to kill the process before we regain control.
sigset_t mask, old_mask;
sigemptyset(&mask);
sigaddset(&mask, uctx->signum);
// raw syscall to bypass libsigchain on Android
syscall(SYS_rt_sigprocmask, SIG_BLOCK, &mask, &old_mask, _NSIG / 8);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Raw syscall buffer overflow on 32-bit Android

High Severity

On 32-bit Android (LP32), bionic's sigset_t is only 4 bytes, but _NSIG / 8 evaluates to 8. The raw syscall(SYS_rt_sigprocmask, SIG_BLOCK, &mask, &old_mask, _NSIG / 8) tells the kernel to write 8 bytes into the 4-byte old_mask buffer, causing a stack buffer overflow. The project explicitly targets armeabi-v7a and x86 (32-bit ABIs). Bionic's libc wrapper normally handles this size mismatch via an internal kernel_sigset_t union, but the raw syscall bypasses that conversion.

Additional Locations (2)
Fix in Cursor Fix in Web


// invoke the previous handler (typically the CLR/Mono
// signal-to-managed-exception handler)
invoke_signal_handler(
Expand All @@ -1578,6 +1587,20 @@ process_ucontext(const sentry_ucontext_t *uctx)
return;
}

// restore our handler
struct sigaction current;
sigaction(uctx->signum, NULL, &current);
if (current.sa_handler == SIG_DFL) {
sigaction(uctx->signum, &g_sigaction, NULL);
}

// consume pending signal
struct timespec timeout = { 0, 0 };
syscall(SYS_rt_sigtimedwait, &mask, NULL, &timeout, _NSIG / 8);
Comment on lines +1598 to +1599
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The function returns early if the chained handler modifies ip or sp, but it does so without unmasking the signal or consuming the pending signal raised by the handler.
Severity: HIGH

Suggested Fix

Before the early return, add the necessary calls to consume the pending signal and restore the original signal mask. Specifically, call syscall(SYS_rt_sigtimedwait, ...) with a zero timeout to consume the signal, and then call syscall(SYS_rt_sigprocmask, SIG_SETMASK, &old_mask, ...) to unmask it.

Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent.
Verify if this is a real issue. If it is, propose a fix; if not, explain why it's not
valid.

Location: src/backends/sentry_backend_inproc.c#L1598-L1599

Potential issue: In the signal handler, a signal is masked before invoking a chained
handler. If this chained handler re-raises the signal (e.g., via `raise(signum)`) and
also modifies the instruction pointer (`ip`) or stack pointer (`sp`), the function will
`return` early. This early return path skips the logic that consumes the now-pending
signal with `sigtimedwait` and unmasks it with `sigprocmask`. This leaves the process
with a pending signal that is still masked, preventing its delivery and potentially
causing undefined behavior or crashes later on.


// unmask
syscall(SYS_rt_sigprocmask, SIG_SETMASK, &old_mask, NULL, _NSIG / 8);

// return from runtime handler; continue processing the crash on the
// signal thread until the worker takes over
}
Expand Down
Loading