diff options
| author | Grzegorz Junka <list1@gjunka.com> | 2026-09-27 19:22:20 +0000 |
|---|---|---|
| committer | Konstantin Belousov <kib@FreeBSD.org> | 2026-09-27 19:22:20 +0000 |
| commit | 16a284b1cdfd45ba99c2723e7f88497e76b235ad (patch) | |
| tree | 60deb76a369b3855f0aaff2f5de81aeba3363f8f /cddl/sbin/tests | |
| parent | 1d94247d0e3fe79fee7902383d27f51b089beb0d (diff) | |
Since 80c7315d17ce ("Restore signal mask in epoll_pwait.") the caller's
signal mask is saved in a local variable, but TDP_OLDMASK is still set
and the TDA_SIGSUSPEND AST is still scheduled. On return to user mode
that AST, or postsig() if a signal is delivered, then installs
td_oldsigmask, which this code never writes and which holds whatever
mask the thread had at its last sigsuspend(2), pselect(2) or ppoll(2).
As a result every epoll_pwait(2)/epoll_pwait2(2) call with a non-NULL
sigmask can leave the thread with a stale signal mask. In addition, the
explicit restore at the end overwrote the return value, so EINTR (and
any other error) was reported to user space as 0.
Save the old mask in td_oldsigmask and let the AST restore it, as
kern_pselect() and kern_poll_kfds() do: schedule TDA_SIGSUSPEND if the
wait was interrupted, so the signal is delivered with the temporary mask
in place, and TDA_PSELECT otherwise. This matches Linux, which restores
the saved mask unless the syscall returns -EINTR.
This deadlocks Bun-based programs such as Claude Code (>= 2.1.269) and
opencode. JavaScriptCore suspends threads for conservative GC stack
scanning by sending SIGPWR and waiting for the target's handler, which
calls sigsuspend(2) with SIGPWR blocked. Afterwards td_oldsigmask
contains SIGPWR, the event loop's next epoll_pwait2(2) (Bun always
passes an empty sigmask) blocks SIGPWR on the main thread, and the next
GC suspend request waits forever.
PR: 298878
Fixes: 80c7315d17ce ("Restore signal mask in epoll_pwait.")
MFC after: 1 week
Diffstat (limited to 'cddl/sbin/tests')
0 files changed, 0 insertions, 0 deletions
