[PATCH 09/11] signal: Dequeue fatal signals

From: Eric W. Biederman

Date: Fri Jun 26 2026 - 13:00:28 EST



Fatal signals are detected early and historically have not been
dequeued. This barely matters as the process exits immediately.

Not dequeuing the signal is visible to userspace inspecting the dying
process through proc and will be to coredumps once we start using
short circuit delivery for them.

To keep things simple always populate siginfo in dequeue_exit_signal
and always pass the dequeueed siginfo to trace_signal_deliver.

In the slim chance that the fatal signal was a posix timer free the
posix timer's sigqueue. In general this is not safe with
tasklist_lock held because tasklist_lock needs to nest under it_lock.
In this case I have read through posixtimer_sigqueue_putref and I can
not find it taking the timer's it_lock.

Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx>
---
include/linux/sched/signal.h | 1 +
kernel/signal.c | 40 ++++++++++++++++++++++++++++--------
2 files changed, 33 insertions(+), 8 deletions(-)

diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h
index 1ea0a89cbef0..df7a3c4530e4 100644
--- a/include/linux/sched/signal.h
+++ b/include/linux/sched/signal.h
@@ -262,6 +262,7 @@ struct signal_struct {
#define SIGNAL_STOP_STOPPED 0x00000001 /* job control stop in effect */
#define SIGNAL_STOP_CONTINUED 0x00000002 /* SIGCONT since WCONTINUED reap */
#define SIGNAL_GROUP_EXIT 0x00000004 /* group exit in progress */
+#define SIGNAL_EXIT_DEQUEUE 0x00000008 /* Dequeue the exit signal */
/*
* Pending notifications to parent.
*/
diff --git a/kernel/signal.c b/kernel/signal.c
index 89075c60b92b..ce3a99573aa9 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -665,6 +665,34 @@ int dequeue_signal(sigset_t *mask, kernel_siginfo_t *info, enum pid_type *type)
}
EXPORT_SYMBOL_GPL(dequeue_signal);

+static int dequeue_exit_signal(
+ struct task_struct *tsk, int exit_code, kernel_siginfo_t *info)
+{
+ struct signal_struct *signal = tsk->signal;
+
+ if (signal->flags & SIGNAL_EXIT_DEQUEUE) {
+ struct sigpending *pending = NULL;
+ struct sigqueue *timer_sigq;
+ int signr = exit_code;
+
+ signal->flags &= ~SIGNAL_EXIT_DEQUEUE;
+
+ pending = sigismember(&tsk->pending.signal, signr) ?
+ &tsk->pending : &signal->shared_pending;
+
+ collect_signal(signr, pending, info, &timer_sigq);
+ if (unlikely(timer_sigq)) {
+ posixtimer_sigqueue_putref(timer_sigq);
+ }
+ return signr;
+ }
+ /* There is no short-circuit signal to dequeue -- fake something */
+ clear_siginfo(info);
+ info->si_signo = SIGKILL;
+ info->si_code = SI_KERNEL;
+ return info->si_signo;
+}
+
static int dequeue_synchronous_signal(kernel_siginfo_t *info)
{
struct task_struct *tsk = current;
@@ -1012,7 +1040,7 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type)
* running and doing things after a slower
* thread has the fatal signal pending.
*/
- signal->flags = SIGNAL_GROUP_EXIT;
+ signal->flags = SIGNAL_GROUP_EXIT | SIGNAL_EXIT_DEQUEUE;
signal->group_exit_code = sig;
signal->group_stop_count = 0;
__for_each_thread(signal, t) {
@@ -2874,15 +2902,11 @@ bool get_signal(struct ksignal *ksig)
signal->group_exec_task) {
if (signal->flags & SIGNAL_GROUP_EXIT)
exit_code = signal->group_exit_code;
- signr = SIGKILL;
sigdelset(&current->pending.signal, SIGKILL);
- trace_signal_deliver(SIGKILL, SEND_SIG_NOINFO,
- &sighand->action[SIGKILL-1]);
+ signr = dequeue_exit_signal(current, exit_code, &ksig->info);
+ trace_signal_deliver(signr, &ksig->info,
+ &sighand->action[signr-1]);
recalc_sigpending();
- /*
- * implies do_group_exit() or return to PF_USER_WORKER,
- * no need to initialize ksig->info/etc.
- */
goto fatal;
}

--
2.41.0