From 4c8bc90ee34008cd45e0dc91327a72002538dc28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edgar=20Arriaga=20Garc=C3=ADa?= Date: Wed, 29 Apr 2020 22:14:21 +0000 Subject: [PATCH] BACKPORT: pidfd: add P_PIDFD to waitid() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This adds the P_PIDFD type to waitid(). One of the last remaining bits for the pidfd api is to make it possible to wait on pidfds. With P_PIDFD added to waitid() the parts of userspace that want to use the pidfd api to exclusively manage processes can do so now. One of the things this will unblock in the future is the ability to make it possible to retrieve the exit status via waitid(P_PIDFD) for non-parent processes if handed a _suitable_ pidfd that has this feature set. This is similar to what you can do on FreeBSD with kqueue(). It might even end up being possible to wait on a process as a non-parent if an appropriate property is enabled on the pidfd. With P_PIDFD no scoping of the process identified by the pidfd is possible, i.e. it explicitly blocks things such as wait4(-1), wait4(0), waitid(P_ALL), waitid(P_PGID) etc. It only allows for semantics equivalent to wait4(pid), waitid(P_PID). Users that need scoping should rely on pid-based wait*() syscalls for now. Signed-off-by: Christian Brauner Reviewed-by: Kees Cook Reviewed-by: Oleg Nesterov Cc: Arnd Bergmann Cc: "Eric W. Biederman" Cc: Joel Fernandes (Google) Cc: Thomas Gleixner Cc: David Howells Cc: Jann Horn Cc: Andy Lutomirsky Cc: Andrew Morton Cc: Aleksa Sarai Cc: Linus Torvalds Cc: Al Viro Link: https://lore.kernel.org/r/20190727222229.6516-2-christian@brauner.io (cherry picked from 3695eae5fee0605f316fbaad0b9e3de791d7dfaf) Conflicts: 1. kernel/exit.c 1. Discarded changes within kernel_waitid since they are not required for process_madvise and increase maintainability cost. Signed-off-by: Edgar Arriaga GarcĂ­a Bug: 153444106 Test: Built kernel Change-Id: Ic8a6834b3943a9b175920abe753c757ee300956f Signed-off-by: azrim --- include/linux/pid.h | 4 ++++ include/uapi/linux/wait.h | 1 + kernel/exit.c | 17 +++++++++++++++++ kernel/fork.c | 8 ++++++++ kernel/signal.c | 7 +++++-- 5 files changed, 35 insertions(+), 2 deletions(-) diff --git a/include/linux/pid.h b/include/linux/pid.h index 0cf3bf22e587..66bbe1361ef4 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -80,6 +80,10 @@ struct pid_link extern const struct file_operations pidfd_fops; +struct file; + +extern struct pid *pidfd_pid(const struct file *file); + static inline struct pid *get_pid(struct pid *pid) { if (pid) diff --git a/include/uapi/linux/wait.h b/include/uapi/linux/wait.h index ac49a220cf2a..85b809fc9f11 100644 --- a/include/uapi/linux/wait.h +++ b/include/uapi/linux/wait.h @@ -17,6 +17,7 @@ #define P_ALL 0 #define P_PID 1 #define P_PGID 2 +#define P_PIDFD 3 #endif /* _UAPI_LINUX_WAIT_H */ diff --git a/kernel/exit.c b/kernel/exit.c index 8a4df91bee75..aa3f1f0afb27 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -1522,6 +1522,23 @@ end: return retval; } +static struct pid *pidfd_get_pid(unsigned int fd) +{ + struct fd f; + struct pid *pid; + + f = fdget(fd); + if (!f.file) + return ERR_PTR(-EBADF); + + pid = pidfd_pid(f.file); + if (!IS_ERR(pid)) + get_pid(pid); + + fdput(f); + return pid; +} + static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop, int options, struct rusage *ru) { diff --git a/kernel/fork.c b/kernel/fork.c index 9f46416259c7..a700daa73cae 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -1551,6 +1551,14 @@ init_task_pid(struct task_struct *task, enum pid_type type, struct pid *pid) task->pids[type].pid = pid; } +struct pid *pidfd_pid(const struct file *file) +{ + if (file->f_op == &pidfd_fops) + return file->private_data; + + return ERR_PTR(-EBADF); +} + static int pidfd_release(struct inode *inode, struct file *file) { struct pid *pid = file->private_data; diff --git a/kernel/signal.c b/kernel/signal.c index 759ef4481003..07c0318968ef 100644 --- a/kernel/signal.c +++ b/kernel/signal.c @@ -3130,8 +3130,11 @@ static bool access_pidfd_pidns(struct pid *pid) static struct pid *pidfd_to_pid(const struct file *file) { - if (file->f_op == &pidfd_fops) - return file->private_data; + struct pid *pid; + + pid = pidfd_pid(file); + if (!IS_ERR(pid)) + return pid; return tgid_pidfd_to_pid(file); }