[PATCH RFC 3/3] pidfd: Add task path ioctls
Chen Linxuan via B4 Relay <[email protected]>
| Newsgroups | org.kernel.vger.linux-fsdevel,org.kernel.feeds.b4-sent,org.kernel.vger.linux-kernel,org.kernel.vger.linux-security-module,org.kvack.linux-mm |
|---|---|
| Message-ID | <[email protected]> |
From: Chen Linxuan <[email protected]> Add PIDFD_GET_EXE, PIDFD_GET_CWD, and PIDFD_GET_ROOT to return close-on-exec O_PATH file descriptors referencing the target task's executable, working directory, and root directory. The new ioctls use the same PTRACE_MODE_READ_FSCREDS permission check and nonzero-argument rejection as the existing namespace ioctls. The target is sampled while holding exec_update_lock so that the access check and path read cannot race with execve(). This allows userspace to obtain stable path references from a pidfd without requiring procfs. Assisted-by: codex:glm-5.3 Signed-off-by: Chen Linxuan <[email protected]> --- fs/pidfs.c | 38 ++++++++++++++++++++++++++++++++++++++ include/uapi/linux/pidfd.h | 7 +++++++ 2 files changed, 45 insertions(+) diff --git a/fs/pidfs.c b/fs/pidfs.c index 39e1e7ad9b2b..95b079d0de0d 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -4,6 +4,7 @@ #include <linux/exportfs.h> #include <linux/file.h> #include <linux/fs.h> +#include <linux/fs_struct.h> #include <linux/cgroup.h> #include <linux/magic.h> #include <linux/mount.h> @@ -510,6 +511,9 @@ static bool pidfs_ioctl_valid(unsigned int cmd) case PIDFD_GET_UTS_NAMESPACE: case PIDFD_GET_USER_NAMESPACE: case PIDFD_GET_PID_NAMESPACE: + case PIDFD_GET_EXE: + case PIDFD_GET_CWD: + case PIDFD_GET_ROOT: return true; } @@ -682,9 +686,31 @@ static struct ns_common *pidfd_get_namespace(struct pid *pid, return ns_common; } +static int pidfd_get_task_path(struct pid *pid, unsigned int cmd, + unsigned long arg, struct path *path) +{ + CLASS(pidfd_task_locked, task)(pid, arg); + + if (IS_ERR(task)) + return PTR_ERR(task); + + switch (cmd) { + case PIDFD_GET_EXE: + return get_task_exe_path(task, path); + case PIDFD_GET_CWD: + return get_task_pwd(task, path); + case PIDFD_GET_ROOT: + return get_task_root(task, path); + } + + return -EINVAL; +} + static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { struct ns_common *ns_common = NULL; + struct path path __free(path_put) = {}; + int error; if (!pidfs_ioctl_valid(cmd)) return -ENOIOCTLCMD; @@ -702,6 +728,18 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (_IOC_NR(cmd) == _IOC_NR(PIDFD_GET_INFO)) return pidfd_info(file, cmd, arg); + switch (cmd) { + case PIDFD_GET_EXE: + case PIDFD_GET_CWD: + case PIDFD_GET_ROOT: + error = pidfd_get_task_path(pidfd_pid(file), cmd, arg, &path); + if (error) + return error; + + return FD_ADD(O_CLOEXEC, + dentry_open(&path, O_PATH, current_cred())); + } + ns_common = pidfd_get_namespace(pidfd_pid(file), cmd, arg); if (IS_ERR(ns_common)) return PTR_ERR(ns_common); diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h index 0919246a1611..95ce1819f423 100644 --- a/include/uapi/linux/pidfd.h +++ b/include/uapi/linux/pidfd.h @@ -121,4 +121,11 @@ struct pidfd_info { #define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10) #define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info) +/* Return an O_PATH file descriptor for the target task's executable. */ +#define PIDFD_GET_EXE _IO(PIDFS_IOCTL_MAGIC, 12) +/* Return an O_PATH file descriptor for the target task's working directory. */ +#define PIDFD_GET_CWD _IO(PIDFS_IOCTL_MAGIC, 13) +/* Return an O_PATH file descriptor for the target task's root directory. */ +#define PIDFD_GET_ROOT _IO(PIDFS_IOCTL_MAGIC, 14) + #endif /* _UAPI_LINUX_PIDFD_H */ -- 2.53.0