Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

pidfd: getfd should always report ESRCH if a task is exiting

We can get EBADF from pidfd_getfd() if a task is currently exiting,
which might be confusing. Let's check PF_EXITING, and just report ESRCH
if so.

I chose PF_EXITING, because it is set in exit_signals(), which is called
before exit_files(). Since ->exit_status is mostly set after
exit_files() in exit_notify(), using that still leaves a window open for
the race.

Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Signed-off-by: Tycho Andersen <tandersen@netflix.com>
Link: https://lore.kernel.org/r/20240206192357.81942-1-tycho@tycho.pizza
Signed-off-by: Christian Brauner <brauner@kernel.org>

authored by

Tycho Andersen and committed by
Christian Brauner
0c9bd6bc 83b290c9

+20 -1
+20 -1
kernel/pid.c
··· 678 678 679 679 up_read(&task->signal->exec_update_lock); 680 680 681 - return file ?: ERR_PTR(-EBADF); 681 + if (!file) { 682 + /* 683 + * It is possible that the target thread is exiting; it can be 684 + * either: 685 + * 1. before exit_signals(), which gives a real fd 686 + * 2. before exit_files() takes the task_lock() gives a real fd 687 + * 3. after exit_files() releases task_lock(), ->files is NULL; 688 + * this has PF_EXITING, since it was set in exit_signals(), 689 + * __pidfd_fget() returns EBADF. 690 + * In case 3 we get EBADF, but that really means ESRCH, since 691 + * the task is currently exiting and has freed its files 692 + * struct, so we fix it up. 693 + */ 694 + if (task->flags & PF_EXITING) 695 + file = ERR_PTR(-ESRCH); 696 + else 697 + file = ERR_PTR(-EBADF); 698 + } 699 + 700 + return file; 682 701 } 683 702 684 703 static int pidfd_getfd(struct pid *pid, int fd)