* [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked()
@ 2026-05-02 14:13 Oleg Nesterov
2026-05-02 14:13 ` [PATCH v2 2/2] signal: prevent evasion of SA_IMMUTABLE signals Oleg Nesterov
` (2 more replies)
0 siblings, 3 replies; 5+ messages in thread
From: Oleg Nesterov @ 2026-05-02 14:13 UTC (permalink / raw)
To: Andrew Morton
Cc: Andy Lutomirski, Kees Cook, Kusaram Devineni, Peter Zijlstra,
Thomas Gleixner, Will Drewry, linux-kernel
force_sig_info_to_task() calls send_signal_locked() but this is pointless;
info is always valid and has_si_pid_and_uid(info) should never be true.
Forced signals do not carry si_pid/si_uid, not to mention that info.si_pid
overlaps with .si_addr/si_call_addr/etc.
Signed-off-by: Oleg Nesterov <oleg@redhat.com>
---
kernel/signal.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/kernel/signal.c b/kernel/signal.c
index 9924489c43a5..75e277e24b52 100644
--- a/kernel/signal.c
+++ b/kernel/signal.c
@@ -1317,7 +1317,7 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t,
if (action->sa.sa_handler == SIG_DFL &&
(!t->ptrace || (handler == HANDLER_EXIT)))
t->signal->flags &= ~SIGNAL_UNKILLABLE;
- ret = send_signal_locked(sig, info, t, PIDTYPE_PID);
+ ret = __send_signal_locked(sig, info, t, PIDTYPE_PID, false);
/* This can happen if the signal was already pending and blocked */
if (!task_sigpending(t))
signal_wake_up(t, 0);
--
2.52.0
^ permalink raw reply related [flat|nested] 5+ messages in thread* [PATCH v2 2/2] signal: prevent evasion of SA_IMMUTABLE signals 2026-05-02 14:13 [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov @ 2026-05-02 14:13 ` Oleg Nesterov 2026-05-04 9:27 ` [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov 2026-05-13 23:52 ` Andrew Morton 2 siblings, 0 replies; 5+ messages in thread From: Oleg Nesterov @ 2026-05-02 14:13 UTC (permalink / raw) To: Andrew Morton Cc: Andy Lutomirski, Kees Cook, Kusaram Devineni, Peter Zijlstra, Thomas Gleixner, Will Drewry, linux-kernel force_sig_info_to_task(HANDLER_EXIT) sets SA_IMMUTABLE to ensure a forced fatal signal cannot be ignored or caught by userspace; it must always terminate the target. However, if get_signal() dequeues another synchronous signal first, and that signal has a handler and its sa_mask includes the fatal SA_IMMUTABLE signal, the task can return to userspace and survive. So dequeue_synchronous_signal() must always dequeue an SA_IMMUTABLE signal first. But it relies on the SI_FROMKERNEL() check and picks the first one it sees in pending->list, and thus we have the following problems: - If the same signal was already pending and blocked, the new siginfo with .si_code > 0 will be lost. Change __send_signal_locked() to bypass the legacy_queue() check in this case. - If force_sig_info_to_task() races with another synchronous/SI_FROMKERNEL signal, that signal can be picked first. Change __send_signal_locked() to add an SA_IMMUTABLE signal at the start of pending->list. - SA_IMMUTABLE implies override_rlimit == true, but GFP_ATOMIC can fail anyway. Change __send_signal_locked() to escalate to SIGKILL in this (very unlikely) case. Not perfect and perhaps deserves WARN() or pr_warn_ratelimited(), but better than nothing. However, unlike get_signal(), __send_signal_locked() can not rely on the k_sigaction.sa.sa_flags & SA_IMMUTABLE check; another signal with the same .si_signo can come before dequeue_synchronous_signal() dequeues the signal sent by force(HANDLER_EXIT). Say, send_sig_perf() from task_work_run(), and this signal is SI_FROMKERNEL() too. So this patch adds 2 helpers, mark_si_immutable() and unmark_si_immutable() for force_sig_info_to_task() and __send_signal_locked() to pass/check the "immutable" state of siginfo sent by HANDLER_EXIT. Signed-off-by: Oleg Nesterov <oleg@redhat.com> --- kernel/signal.c | 35 ++++++++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 5 deletions(-) diff --git a/kernel/signal.c b/kernel/signal.c index 75e277e24b52..5a5eeb04ac7a 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1034,6 +1034,19 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type) return; } +static inline void mark_si_immutable(struct kernel_siginfo *info) +{ + info->si_signo |= INT_MIN; /* sets MSB */ +} + +static inline bool unmark_si_immutable(struct kernel_siginfo *info) +{ + bool ret = !is_si_special(info) && (info->si_signo & INT_MIN); + if (ret) + info->si_signo &= ~INT_MIN; + return ret; +} + static inline bool legacy_queue(struct sigpending *signals, int sig) { return (sig < SIGRTMIN) && sigismember(&signals->signal, sig); @@ -1042,6 +1055,7 @@ static inline bool legacy_queue(struct sigpending *signals, int sig) static int __send_signal_locked(int sig, struct kernel_siginfo *info, struct task_struct *t, enum pid_type type, bool force) { + bool immutable = unmark_si_immutable(info); struct sigpending *pending; struct sigqueue *q; int override_rlimit; @@ -1055,12 +1069,12 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, pending = (type != PIDTYPE_PID) ? &t->signal->shared_pending : &t->pending; /* - * Short-circuit ignored signals and support queuing - * exactly one non-rt signal, so that we can get more - * detailed information about the cause of the signal. + * Queue exactly one non-rt signal so that we can get more + * detailed information about the cause. But we must never + * lose the siginfo for an SA_IMMUTABLE signal. */ result = TRACE_SIGNAL_ALREADY_PENDING; - if (legacy_queue(pending, sig)) + if (legacy_queue(pending, sig) && !immutable) goto ret; result = TRACE_SIGNAL_DELIVERED; @@ -1087,7 +1101,12 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, q = sigqueue_alloc(sig, t, GFP_ATOMIC, override_rlimit); if (q) { - list_add_tail(&q->list, &pending->list); + /* Ensure dequeue_synchronous_signal() sees SA_IMMUTABLE first */ + if (immutable) + list_add(&q->list, &pending->list); + else + list_add_tail(&q->list, &pending->list); + switch ((unsigned long) info) { case (unsigned long) SEND_SIG_NOINFO: clear_siginfo(&q->info); @@ -1130,6 +1149,9 @@ static int __send_signal_locked(int sig, struct kernel_siginfo *info, * send the signal, but the *info bits are lost. */ result = TRACE_SIGNAL_LOSE_INFO; + /* The task must not escape SA_IMMUTABLE; escalate to SIGKILL */ + if (immutable) + sig = SIGKILL; } out_set: @@ -1317,6 +1339,9 @@ force_sig_info_to_task(struct kernel_siginfo *info, struct task_struct *t, if (action->sa.sa_handler == SIG_DFL && (!t->ptrace || (handler == HANDLER_EXIT))) t->signal->flags &= ~SIGNAL_UNKILLABLE; + + if (handler == HANDLER_EXIT) + mark_si_immutable(info); ret = __send_signal_locked(sig, info, t, PIDTYPE_PID, false); /* This can happen if the signal was already pending and blocked */ if (!task_sigpending(t)) -- 2.52.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() 2026-05-02 14:13 [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov 2026-05-02 14:13 ` [PATCH v2 2/2] signal: prevent evasion of SA_IMMUTABLE signals Oleg Nesterov @ 2026-05-04 9:27 ` Oleg Nesterov 2026-05-13 23:52 ` Andrew Morton 2 siblings, 0 replies; 5+ messages in thread From: Oleg Nesterov @ 2026-05-04 9:27 UTC (permalink / raw) To: Andrew Morton Cc: Andy Lutomirski, Kees Cook, Kusaram Devineni, Peter Zijlstra, Thomas Gleixner, Will Drewry, linux-kernel On 05/02, Oleg Nesterov wrote: > > force_sig_info_to_task() calls send_signal_locked() but this is pointless; > info is always valid and has_si_pid_and_uid(info) should never be true. > Forced signals do not carry si_pid/si_uid, not to mention that info.si_pid > overlaps with .si_addr/si_call_addr/etc. https://sashiko.dev/#/patchset/afYGeHSWkLzXDlvC%40redhat.com Sorry I could not reply earlier... Oh, I didn't know people use force_sig() to send SIGKILL. I'd say they should not, but this doesn't matter - they do. So this needs yet another version. And I will read man grep 3 times. Thanks again Sashiko. Oleg. ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() 2026-05-02 14:13 [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov 2026-05-02 14:13 ` [PATCH v2 2/2] signal: prevent evasion of SA_IMMUTABLE signals Oleg Nesterov 2026-05-04 9:27 ` [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov @ 2026-05-13 23:52 ` Andrew Morton 2026-05-14 8:18 ` Oleg Nesterov 2 siblings, 1 reply; 5+ messages in thread From: Andrew Morton @ 2026-05-13 23:52 UTC (permalink / raw) To: Oleg Nesterov Cc: Andy Lutomirski, Kees Cook, Kusaram Devineni, Peter Zijlstra, Thomas Gleixner, Will Drewry, linux-kernel On Sat, 2 May 2026 16:13:12 +0200 Oleg Nesterov <oleg@redhat.com> wrote: > force_sig_info_to_task() calls send_signal_locked() but this is pointless; > info is always valid and has_si_pid_and_uid(info) should never be true. > Forced signals do not carry si_pid/si_uid, not to mention that info.si_pid > overlaps with .si_addr/si_call_addr/etc. Thanks. AI review said things: https://sashiko.dev/#/patchset/afYGeHSWkLzXDlvC@redhat.com ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() 2026-05-13 23:52 ` Andrew Morton @ 2026-05-14 8:18 ` Oleg Nesterov 0 siblings, 0 replies; 5+ messages in thread From: Oleg Nesterov @ 2026-05-14 8:18 UTC (permalink / raw) To: Andrew Morton Cc: Andy Lutomirski, Kees Cook, Kusaram Devineni, Peter Zijlstra, Thomas Gleixner, Will Drewry, linux-kernel On 05/13, Andrew Morton wrote: > > On Sat, 2 May 2026 16:13:12 +0200 Oleg Nesterov <oleg@redhat.com> wrote: > > > force_sig_info_to_task() calls send_signal_locked() but this is pointless; > > info is always valid and has_si_pid_and_uid(info) should never be true. > > Forced signals do not carry si_pid/si_uid, not to mention that info.si_pid > > overlaps with .si_addr/si_call_addr/etc. > > Thanks. AI review said things: > https://sashiko.dev/#/patchset/afYGeHSWkLzXDlvC@redhat.com Yes, yes, thanks. I have already replied, https://lore.kernel.org/all/afhmbCryeyIz0E3V@redhat.com/ Sashiko is right. Again!!! This needs another version. Oleg. ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-05-14 8:19 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-05-02 14:13 [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov 2026-05-02 14:13 ` [PATCH v2 2/2] signal: prevent evasion of SA_IMMUTABLE signals Oleg Nesterov 2026-05-04 9:27 ` [PATCH v2 1/2] signal: change force_sig_info_to_task() to call __send_signal_locked() Oleg Nesterov 2026-05-13 23:52 ` Andrew Morton 2026-05-14 8:18 ` Oleg Nesterov
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.