Add a helper schedule_task_exit_locked that is equivalent to asynchronously calling exit(2) except for not having an exit code. This is a generalization of what happens in de_thread, zap_process, prepare_signal, complete_signal, and zap_other_threads when individual tasks are asked to shutdown. The various code paths optimize away the setting sigaddset and signal_wake_up based on different conditions. Neither sigaddset nor signal_wake_up are needed if the task has already started running do_exit. So skip the work if PF_POSTCOREDUMP is set. Which is the earliest any of the original hand rolled implementations used. Update get_signal to detect either signal group exit or a single task exit by testing for __fatal_signal_pending. This works because the all of the tasks in group exits are killed with schedule_task_exit_locked. For clarity the code in get_signal has been updated to call do_exit instead of do_group_exit when a single task is exiting. While this schedule_task_exit_locked is a generalization of what happens in prepare_signal I do not change prepare_signal to use schedule_task_exit_locked to deliver SIGKILL to a coredumping process. This keeps all of the specialness delivering a signal to a coredumping process limited to prepare_signal and the coredump code itself. Signed-off-by: "Eric W. Biederman" <ebiederm@xxxxxxxxxxxx> --- fs/coredump.c | 7 ++----- include/linux/sched/signal.h | 2 ++ kernel/signal.c | 36 +++++++++++++++++++++--------------- 3 files changed, 25 insertions(+), 20 deletions(-) diff --git a/fs/coredump.c b/fs/coredump.c index 09302a6a0d80..9559e29daada 100644 --- a/fs/coredump.c +++ b/fs/coredump.c @@ -358,12 +358,9 @@ static int zap_process(struct task_struct *start, int exit_code) start->signal->group_stop_count = 0; for_each_thread(start, t) { - task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); - if (t != current && !(t->flags & PF_POSTCOREDUMP)) { - sigaddset(&t->pending.signal, SIGKILL); - signal_wake_up(t, 1); + schedule_task_exit_locked(t); + if (t != current && !(t->flags & PF_POSTCOREDUMP)) nr++; - } } return nr; diff --git a/include/linux/sched/signal.h b/include/linux/sched/signal.h index b6ecb9fc4cd2..7c62b7c29cc0 100644 --- a/include/linux/sched/signal.h +++ b/include/linux/sched/signal.h @@ -427,6 +427,8 @@ static inline void ptrace_signal_wake_up(struct task_struct *t, bool resume) signal_wake_up_state(t, resume ? __TASK_TRACED : 0); } +void schedule_task_exit_locked(struct task_struct *task); + void task_join_group_stop(struct task_struct *task); #ifdef TIF_RESTORE_SIGMASK diff --git a/kernel/signal.c b/kernel/signal.c index 2a24cca00ca1..cbfb9020368e 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -1056,9 +1056,7 @@ static void complete_signal(int sig, struct task_struct *p, enum pid_type type) signal->group_stop_count = 0; t = p; do { - task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); - sigaddset(&t->pending.signal, SIGKILL); - signal_wake_up(t, 1); + schedule_task_exit_locked(t); } while_each_thread(p, t); return; } @@ -1363,6 +1361,16 @@ int force_sig_info(struct kernel_siginfo *info) return force_sig_info_to_task(info, current, HANDLER_CURRENT); } +void schedule_task_exit_locked(struct task_struct *task) +{ + task_clear_jobctl_pending(task, JOBCTL_PENDING_MASK); + /* Only bother with threads that might be alive */ + if (!(task->flags & PF_POSTCOREDUMP)) { + sigaddset(&task->pending.signal, SIGKILL); + signal_wake_up(task, 1); + } +} + /* * Nuke all other threads in the group. */ @@ -1374,16 +1382,9 @@ int zap_other_threads(struct task_struct *p) p->signal->group_stop_count = 0; while_each_thread(p, t) { - task_clear_jobctl_pending(t, JOBCTL_PENDING_MASK); count++; - - /* Don't bother with already dead threads */ - if (t->exit_state) - continue; - sigaddset(&t->pending.signal, SIGKILL); - signal_wake_up(t, 1); + schedule_task_exit_locked(t); } - return count; } @@ -2706,12 +2707,12 @@ bool get_signal(struct ksignal *ksig) for (;;) { struct k_sigaction *ka; + bool group_exit = true; enum pid_type type; int exit_code; /* Has this task already been marked for death? */ - if ((signal->flags & SIGNAL_GROUP_EXIT) || - signal->group_exec_task) { + if (__fatal_signal_pending(current)) { ksig->info.si_signo = signr = SIGKILL; sigdelset(¤t->pending.signal, SIGKILL); trace_signal_deliver(SIGKILL, SEND_SIG_NOINFO, @@ -2719,8 +2720,10 @@ bool get_signal(struct ksignal *ksig) recalc_sigpending(); if (signal->flags & SIGNAL_GROUP_EXIT) exit_code = signal->group_exit_code; - else + else { exit_code = 0; + group_exit = false; + } goto fatal; } @@ -2880,7 +2883,10 @@ bool get_signal(struct ksignal *ksig) /* * Death signals, no core dump. */ - do_group_exit(exit_code); + if (group_exit) + do_group_exit(exit_code); + else + do_exit(exit_code); /* NOTREACHED */ } spin_unlock_irq(&sighand->siglock); -- 2.29.2