Skip to content

Commit caf7743

Browse files
committed
signal: Handle ignored signals in do_sigaction(action != SIG_IGN)
When a real handler (including SIG_DFL) is installed for a signal, which had previously SIG_IGN set, then the list of ignored posix timers has to be checked for timers which are affected by this change. Add a list walk function which checks for the matching signal number and if found requeues the timers signal, so the timer is rearmed on signal delivery. Rearming the timer right away is not possible because that requires to drop sighand lock. No functional change as the counter part which queues the timers on the ignored list is still missing. Signed-off-by: Thomas Gleixner <[email protected]> Reviewed-by: Frederic Weisbecker <[email protected]> Acked-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lore.kernel.org/all/[email protected]
1 parent 0e20cd3 commit caf7743

File tree

1 file changed

+52
-1
lines changed

1 file changed

+52
-1
lines changed

kernel/signal.c

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2002,7 +2002,54 @@ int posixtimer_send_sigqueue(struct k_itimer *tmr)
20022002
unlock_task_sighand(t, &flags);
20032003
return ret;
20042004
}
2005-
#endif /* CONFIG_POSIX_TIMERS */
2005+
2006+
static void posixtimer_sig_unignore(struct task_struct *tsk, int sig)
2007+
{
2008+
struct hlist_head *head = &tsk->signal->ignored_posix_timers;
2009+
struct hlist_node *tmp;
2010+
struct k_itimer *tmr;
2011+
2012+
if (likely(hlist_empty(head)))
2013+
return;
2014+
2015+
/*
2016+
* Rearming a timer with sighand lock held is not possible due to
2017+
* lock ordering vs. tmr::it_lock. Just stick the sigqueue back and
2018+
* let the signal delivery path deal with it whether it needs to be
2019+
* rearmed or not. This cannot be decided here w/o dropping sighand
2020+
* lock and creating a loop retry horror show.
2021+
*/
2022+
hlist_for_each_entry_safe(tmr, tmp , head, ignored_list) {
2023+
struct task_struct *target;
2024+
2025+
/*
2026+
* tmr::sigq.info.si_signo is immutable, so accessing it
2027+
* without holding tmr::it_lock is safe.
2028+
*/
2029+
if (tmr->sigq.info.si_signo != sig)
2030+
continue;
2031+
2032+
hlist_del_init(&tmr->ignored_list);
2033+
2034+
/* This should never happen and leaks a reference count */
2035+
if (WARN_ON_ONCE(!list_empty(&tmr->sigq.list)))
2036+
continue;
2037+
2038+
/*
2039+
* Get the target for the signal. If target is a thread and
2040+
* has exited by now, drop the reference count.
2041+
*/
2042+
guard(rcu)();
2043+
target = posixtimer_get_target(tmr);
2044+
if (target)
2045+
posixtimer_queue_sigqueue(&tmr->sigq, target, tmr->it_pid_type);
2046+
else
2047+
posixtimer_putref(tmr);
2048+
}
2049+
}
2050+
#else /* CONFIG_POSIX_TIMERS */
2051+
static inline void posixtimer_sig_unignore(struct task_struct *tsk, int sig) { }
2052+
#endif /* !CONFIG_POSIX_TIMERS */
20062053

20072054
void do_notify_pidfd(struct task_struct *task)
20082055
{
@@ -4180,6 +4227,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
41804227
sigaction_compat_abi(act, oact);
41814228

41824229
if (act) {
4230+
bool was_ignored = k->sa.sa_handler == SIG_IGN;
4231+
41834232
sigdelsetmask(&act->sa.sa_mask,
41844233
sigmask(SIGKILL) | sigmask(SIGSTOP));
41854234
*k = *act;
@@ -4200,6 +4249,8 @@ int do_sigaction(int sig, struct k_sigaction *act, struct k_sigaction *oact)
42004249
flush_sigqueue_mask(p, &mask, &p->signal->shared_pending);
42014250
for_each_thread(p, t)
42024251
flush_sigqueue_mask(p, &mask, &t->pending);
4252+
} else if (was_ignored) {
4253+
posixtimer_sig_unignore(p, sig);
42034254
}
42044255
}
42054256

0 commit comments

Comments
 (0)