Skip to content

Commit d80f7d7

Browse files
committed
signal: Guarantee that SIGNAL_GROUP_EXIT is set on process exit
Track how many threads have not started exiting and when the last thread starts exiting set SIGNAL_GROUP_EXIT. This guarantees that SIGNAL_GROUP_EXIT will get set when a process exits. In practice this achieves nothing as glibc's implementation of _exit calls sys_group_exit then sys_exit. While glibc's implemenation of pthread_exit calls exit (which cleansup and calls _exit) if it is the last thread and sys_exit if it is the last thread. This means the only way the kernel might observe a process that does not set call exit_group is if the language runtime does not use glibc. With more cleanups I hope to move the decrement of quick_threads earlier. Link: https://lkml.kernel.org/r/[email protected] Signed-off-by: "Eric W. Biederman" <[email protected]>
1 parent cbe9dac commit d80f7d7

File tree

3 files changed

+21
-0
lines changed

3 files changed

+21
-0
lines changed

include/linux/sched/signal.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,7 @@ struct signal_struct {
9494
refcount_t sigcnt;
9595
atomic_t live;
9696
int nr_threads;
97+
int quick_threads;
9798
struct list_head thread_head;
9899

99100
wait_queue_head_t wait_chldexit; /* for wait4() */

kernel/exit.c

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -733,11 +733,29 @@ static void check_stack_usage(void)
733733
static inline void check_stack_usage(void) {}
734734
#endif
735735

736+
static void synchronize_group_exit(struct task_struct *tsk, long code)
737+
{
738+
struct sighand_struct *sighand = tsk->sighand;
739+
struct signal_struct *signal = tsk->signal;
740+
741+
spin_lock_irq(&sighand->siglock);
742+
signal->quick_threads--;
743+
if ((signal->quick_threads == 0) &&
744+
!(signal->flags & SIGNAL_GROUP_EXIT)) {
745+
signal->flags = SIGNAL_GROUP_EXIT;
746+
signal->group_exit_code = code;
747+
signal->group_stop_count = 0;
748+
}
749+
spin_unlock_irq(&sighand->siglock);
750+
}
751+
736752
void __noreturn do_exit(long code)
737753
{
738754
struct task_struct *tsk = current;
739755
int group_dead;
740756

757+
synchronize_group_exit(tsk, code);
758+
741759
WARN_ON(tsk->plug);
742760

743761
kcov_task_exit(tsk);

kernel/fork.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,6 +1692,7 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk)
16921692
return -ENOMEM;
16931693

16941694
sig->nr_threads = 1;
1695+
sig->quick_threads = 1;
16951696
atomic_set(&sig->live, 1);
16961697
refcount_set(&sig->sigcnt, 1);
16971698

@@ -2444,6 +2445,7 @@ static __latent_entropy struct task_struct *copy_process(
24442445
__this_cpu_inc(process_counts);
24452446
} else {
24462447
current->signal->nr_threads++;
2448+
current->signal->quick_threads++;
24472449
atomic_inc(&current->signal->live);
24482450
refcount_inc(&current->signal->sigcnt);
24492451
task_join_group_stop(p);

0 commit comments

Comments
 (0)