signals: a blocked signal stays pending instead of killing

sigsend() ended a process for a default-fatal signal (SIGUSR1, SIGTERM,
SIGINT...) on arrival, whatever its signal mask said. Blocking is
exactly the promise that it will not: the signal waits until it is
unblocked, or until the program takes it some other way -- which is
what signalfd is, and how this was found.

A blocked signal is now only made pending. If it is later unblocked
while still pending, deliver_signals() applies the default action
then. SIGKILL cannot be blocked, as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
This commit is contained in:
Nexoniarz 2026-10-24 10:59:52 +02:00
commit 6bfb367090
3 changed files with 40 additions and 12 deletions

View file

@ -191,8 +191,17 @@ deliver_signals(struct trapframe *tf)
// applied by sigsend(); all that is left here is to stop it being
// pending, which the old `continue` never did, so an ignored signal
// was re-examined on the way out of every syscall forever after.
//
// Except for a signal that was blocked when it arrived: sigsend()
// left it pending without acting, and now that it is deliverable its
// default action happens here.
if (myproc()->sig_handler[sig] <= 1) {
myproc()->pending_signals &= ~(1ULL << sig);
if (myproc()->sig_handler[sig] == 0 && sig_default_kills(sig)) {
myproc()->killed = 1;
if (myproc()->killsig == 0)
myproc()->killsig = sig;
}
continue;
}