mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 02:30:34 +02:00 
			
		
		
		
	pidfd: fix a poll race when setting exit_state
There is a race between reading task->exit_state in pidfd_poll and
writing it after do_notify_parent calls do_notify_pidfd. Expected
sequence of events is:
CPU 0                            CPU 1
------------------------------------------------
exit_notify
  do_notify_parent
    do_notify_pidfd
  tsk->exit_state = EXIT_DEAD
                                  pidfd_poll
                                     if (tsk->exit_state)
However nothing prevents the following sequence:
CPU 0                            CPU 1
------------------------------------------------
exit_notify
  do_notify_parent
    do_notify_pidfd
                                   pidfd_poll
                                      if (tsk->exit_state)
  tsk->exit_state = EXIT_DEAD
This causes a polling task to wait forever, since poll blocks because
exit_state is 0 and the waiting task is not notified again. A stress
test continuously doing pidfd poll and process exits uncovered this bug.
To fix it, we make sure that the task's exit_state is always set before
calling do_notify_pidfd.
Fixes: b53b0b9d9a ("pidfd: add polling support")
Cc: kernel-team@android.com
Cc: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Suren Baghdasaryan <surenb@google.com>
Signed-off-by: Joel Fernandes (Google) <joel@joelfernandes.org>
Link: https://lore.kernel.org/r/20190717172100.261204-1-joel@joelfernandes.org
[christian@brauner.io: adapt commit message and drop unneeded changes from wait_task_zombie]
Signed-off-by: Christian Brauner <christian@brauner.io>
			
			
This commit is contained in:
		
							parent
							
								
									5f9e832c13
								
							
						
					
					
						commit
						b191d6491b
					
				
					 1 changed files with 1 additions and 0 deletions
				
			
		| 
						 | 
					@ -720,6 +720,7 @@ static void exit_notify(struct task_struct *tsk, int group_dead)
 | 
				
			||||||
	if (group_dead)
 | 
						if (group_dead)
 | 
				
			||||||
		kill_orphaned_pgrp(tsk->group_leader, NULL);
 | 
							kill_orphaned_pgrp(tsk->group_leader, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tsk->exit_state = EXIT_ZOMBIE;
 | 
				
			||||||
	if (unlikely(tsk->ptrace)) {
 | 
						if (unlikely(tsk->ptrace)) {
 | 
				
			||||||
		int sig = thread_group_leader(tsk) &&
 | 
							int sig = thread_group_leader(tsk) &&
 | 
				
			||||||
				thread_group_empty(tsk) &&
 | 
									thread_group_empty(tsk) &&
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in a new issue