pidfd: add P_PIDFD to waitid()
authorChristian Brauner <christian.brauner@ubuntu.com>
Sat, 27 Jul 2019 22:22:29 +0000 (00:22 +0200)
committerChristian Brauner <christian.brauner@ubuntu.com>
Thu, 1 Aug 2019 19:49:46 +0000 (21:49 +0200)
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 <christian.brauner@ubuntu.com>
Reviewed-by: Kees Cook <keescook@chromium.org>
Reviewed-by: Oleg Nesterov <oleg@redhat.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: "Eric W. Biederman" <ebiederm@xmission.com>
Cc: Joel Fernandes (Google) <joel@joelfernandes.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: David Howells <dhowells@redhat.com>
Cc: Jann Horn <jannh@google.com>
Cc: Andy Lutomirsky <luto@kernel.org>
Cc: Andrew Morton <akpm@linux-foundation.org>
Cc: Aleksa Sarai <cyphar@cyphar.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Link: https://lore.kernel.org/r/20190727222229.6516-2-christian@brauner.io
include/linux/pid.h
include/uapi/linux/wait.h
kernel/exit.c
kernel/fork.c
kernel/signal.c

index 2a83e434db9d080667bd8f3465552c797e814c10..9645b1194c9812adc23c62b9518c305c3dcacf40 100644 (file)
@@ -72,6 +72,10 @@ extern struct pid init_struct_pid;
 
 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)
index ac49a220cf2ad8ee3fa1bfa2b72c9b232c1cf0d6..85b809fc9f118414a33eb730c21b62c35a91dd12 100644 (file)
@@ -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 */
index a75b6a7f458a7287439e40f0ac102fbb8b61e4a4..64bb6893a37d8f7b5341c7c66661c382652cba9b 100644 (file)
@@ -1552,6 +1552,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)
 {
@@ -1574,19 +1591,29 @@ static long kernel_waitid(int which, pid_t upid, struct waitid_info *infop,
                type = PIDTYPE_PID;
                if (upid <= 0)
                        return -EINVAL;
+
+               pid = find_get_pid(upid);
                break;
        case P_PGID:
                type = PIDTYPE_PGID;
                if (upid <= 0)
                        return -EINVAL;
+
+               pid = find_get_pid(upid);
+               break;
+       case P_PIDFD:
+               type = PIDTYPE_PID;
+               if (upid < 0)
+                       return -EINVAL;
+
+               pid = pidfd_get_pid(upid);
+               if (IS_ERR(pid))
+                       return PTR_ERR(pid);
                break;
        default:
                return -EINVAL;
        }
 
-       if (type < PIDTYPE_MAX)
-               pid = find_get_pid(upid);
-
        wo.wo_type      = type;
        wo.wo_pid       = pid;
        wo.wo_flags     = options;
index d8ae0f1b4148023c8c0d242b799039deb362ca09..b169e2ca2d84d3f08e5003b38c46428775c20230 100644 (file)
@@ -1690,6 +1690,14 @@ static inline void rcu_copy_process(struct task_struct *p)
 #endif /* #ifdef CONFIG_TASKS_RCU */
 }
 
+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;
index 91b789dd6e722ef96f8cf4440eb8f5bbc857ac60..2e567f64812f31e4e80e53e9fc7d77095c9c2af1 100644 (file)
@@ -3672,8 +3672,11 @@ static int copy_siginfo_from_user_any(kernel_siginfo_t *kinfo, siginfo_t *info)
 
 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);
 }