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:
parent
5ed8f1b0d5
commit
6bfb367090
3 changed files with 40 additions and 12 deletions
|
|
@ -1508,6 +1508,34 @@ init_ignores(struct proc *p, int sig)
|
|||
return p == initproc && p->sig_handler[sig] == 0;
|
||||
}
|
||||
|
||||
// Whether a signal's default action is to end the process.
|
||||
int
|
||||
sig_default_kills(int sig)
|
||||
{
|
||||
return sig == SIGKILL || sig == SIGTERM || sig == SIGABRT ||
|
||||
sig == SIGSEGV || sig == SIGFPE || sig == SIGBUS ||
|
||||
sig == SIGILL || sig == SIGINT || sig == SIGQUIT ||
|
||||
sig == SIGHUP || sig == SIGPIPE || sig == SIGUSR1 ||
|
||||
sig == SIGUSR2 || sig == SIGALRM;
|
||||
}
|
||||
|
||||
// Whether sending `sig` ends `p` now: no handler, a default action that
|
||||
// kills, and **not blocked**. A blocked signal stays pending, which is the
|
||||
// whole of what blocking means -- and the contract signalfd is built on: a
|
||||
// program blocks SIGUSR1 and reads it from a descriptor instead. This used to
|
||||
// kill on arrival whatever the mask said. deliver_signals() applies the
|
||||
// default action if the signal is unblocked later, still pending.
|
||||
// SIGKILL cannot be blocked.
|
||||
static int
|
||||
sig_kills_now(struct proc *p, int sig)
|
||||
{
|
||||
if(sig == SIGKILL)
|
||||
return 1;
|
||||
if(p->signal_mask & (1ULL << sig))
|
||||
return 0;
|
||||
return p->sig_handler[sig] == 0 && sig_default_kills(sig);
|
||||
}
|
||||
|
||||
int
|
||||
sigsend(int pid, int sig)
|
||||
{
|
||||
|
|
@ -1524,12 +1552,7 @@ sigsend(int pid, int sig)
|
|||
return 0; // silently dropped, as POSIX requires
|
||||
}
|
||||
p->pending_signals |= 1ULL << sig;
|
||||
if(p->sig_handler[sig] == 0 &&
|
||||
(sig == SIGKILL || sig == SIGTERM || sig == SIGABRT ||
|
||||
sig == SIGSEGV || sig == SIGFPE || sig == SIGBUS ||
|
||||
sig == SIGILL || sig == SIGINT || sig == SIGQUIT ||
|
||||
sig == SIGHUP || sig == SIGPIPE || sig == SIGUSR1 ||
|
||||
sig == SIGUSR2 || sig == SIGALRM))
|
||||
if(sig_kills_now(p, sig))
|
||||
p->killed = 1;
|
||||
if(sig == SIGKILL)
|
||||
p->killed = 1;
|
||||
|
|
@ -1573,12 +1596,7 @@ sigsend_pgrp(int pgid, int sig)
|
|||
if(init_ignores(p, sig))
|
||||
continue; // see init_ignores(): this is the Ctrl-C case
|
||||
p->pending_signals |= 1ULL << sig;
|
||||
if(p->sig_handler[sig] == 0 &&
|
||||
(sig == SIGKILL || sig == SIGTERM || sig == SIGABRT ||
|
||||
sig == SIGSEGV || sig == SIGFPE || sig == SIGBUS ||
|
||||
sig == SIGILL || sig == SIGINT || sig == SIGQUIT ||
|
||||
sig == SIGHUP || sig == SIGPIPE || sig == SIGUSR1 ||
|
||||
sig == SIGUSR2 || sig == SIGALRM)){
|
||||
if(sig_kills_now(p, sig)){
|
||||
p->killed = 1;
|
||||
if(p->killsig == 0)
|
||||
p->killsig = sig;
|
||||
|
|
|
|||
Loading…
Reference in a new issue