From: luca.boccassi@gmail.com
To: linux-kernel@vger.kernel.org
Cc: christian@brauner.io, paul@paul-moore.com
Subject: [PATCH] pidfd: add ioctl to retrieve pid info
Date: Wed, 2 Oct 2024 15:24:33 +0100 [thread overview]
Message-ID: <20241002142516.110567-1-luca.boccassi@gmail.com> (raw)
From: Luca Boccassi <bluca@debian.org>
A common pattern when using pid fds is having to get information
about the process, which currently requires /proc being mounted,
resolving the fd to a pid, and then do manual string parsing of
/proc/N/status and friends. This needs to be reimplemented over
and over in all userspace projects (e.g.: I have reimplemented
resolving in systemd, dbus, dbus-daemon, polkit so far), and
requires additional care in checking that the fd is still valid
after having parsed the data, to avoid races.
Having a programmatic API that can be used directly removes all
these requirements, including having /proc mounted.
As discussed at LPC24, add an ioctl with an extensible struct
so that more parameters can be added later if needed. Start with
exposing: pid, uid, gid, groupid, security label (the latter was
requested by the LSM maintainer).
Signed-off-by: Luca Boccassi <bluca@debian.org>
---
fs/pidfs.c | 61 ++++++++++++++++++-
include/uapi/linux/pidfd.h | 17 ++++++
.../testing/selftests/pidfd/pidfd_open_test.c | 50 ++++++++++++++-
3 files changed, 126 insertions(+), 2 deletions(-)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 7ffdc88dfb52..dd386d37309c 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -114,6 +114,62 @@ static __poll_t pidfd_poll(struct file *file, struct poll_table_struct *pts)
return poll_flags;
}
+static long pidfd_info(struct task_struct *task, struct pid *pid, unsigned long arg)
+{
+ struct pidfd_info uinfo = {}, info = {};
+
+ if (copy_from_user(&uinfo, (struct pidfd_info *)arg, sizeof(struct pidfd_info)))
+ return -EFAULT;
+ if (uinfo.size > sizeof(struct pidfd_info))
+ return -E2BIG;
+ if (uinfo.size < sizeof(struct pidfd_info))
+ return -EINVAL; /* First version, no smaller struct possible */
+
+ if (uinfo.request_mask & ~(PIDFD_INFO_PID | PIDFD_INFO_CREDS | PIDFD_INFO_CGROUPID | PIDFD_INFO_SECURITY_CONTEXT))
+ return -EINVAL;
+
+ memcpy(&info, &uinfo, uinfo.size);
+
+ if (uinfo.request_mask & PIDFD_INFO_PID)
+ info.pid = pid_nr_ns(pid, task_active_pid_ns(task));
+
+ if (uinfo.request_mask & PIDFD_INFO_CREDS) {
+ const struct cred *c = get_task_cred(task);
+ if (!c)
+ return -ESRCH;
+
+ info.uid = from_kuid_munged(current_user_ns(), c->uid);
+ info.gid = from_kgid_munged(current_user_ns(), c->gid);
+ }
+
+ if (uinfo.request_mask & PIDFD_INFO_CGROUPID) {
+ struct cgroup *cgrp = task_css_check(task, pids_cgrp_id, 1)->cgroup;
+ if (!cgrp)
+ return -ENODEV;
+
+ info.cgroupid = cgroup_id(cgrp);
+ }
+
+ if (uinfo.request_mask & PIDFD_INFO_SECURITY_CONTEXT) {
+ char *secctx;
+ u32 secid, secctx_len;
+ const struct cred *c = get_task_cred(task);
+ if (!c)
+ return -ESRCH;
+
+ security_cred_getsecid(c, &secid);
+ if (security_secid_to_secctx(secid, &secctx, &secctx_len))
+ return -EFAULT;
+
+ memcpy(info.security_context, secctx, min_t(u32, secctx_len, NAME_MAX-1));
+ }
+
+ if (copy_to_user((void __user *)arg, &info, uinfo.size))
+ return -EFAULT;
+
+ return 0;
+}
+
static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct task_struct *task __free(put_task) = NULL;
@@ -121,13 +177,16 @@ static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
struct pid *pid = pidfd_pid(file);
struct ns_common *ns_common = NULL;
- if (arg)
+ if (!!arg != (cmd == PIDFD_GET_INFO))
return -EINVAL;
task = get_pid_task(pid, PIDTYPE_PID);
if (!task)
return -ESRCH;
+ if (cmd == PIDFD_GET_INFO)
+ return pidfd_info(task, pid, arg);
+
scoped_guard(task_lock, task) {
nsp = task->nsproxy;
if (nsp)
diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h
index 565fc0629fff..bfd0965e01f3 100644
--- a/include/uapi/linux/pidfd.h
+++ b/include/uapi/linux/pidfd.h
@@ -16,6 +16,22 @@
#define PIDFD_SIGNAL_THREAD_GROUP (1UL << 1)
#define PIDFD_SIGNAL_PROCESS_GROUP (1UL << 2)
+/* Flags for pidfd_info. */
+#define PIDFD_INFO_PID (1UL << 0)
+#define PIDFD_INFO_CREDS (1UL << 1)
+#define PIDFD_INFO_CGROUPID (1UL << 2)
+#define PIDFD_INFO_SECURITY_CONTEXT (1UL << 3)
+
+struct pidfd_info {
+ __u64 request_mask;
+ __u32 size;
+ uint pid;
+ uint uid;
+ uint gid;
+ __u64 cgroupid;
+ char security_context[NAME_MAX];
+} __packed;
+
#define PIDFS_IOCTL_MAGIC 0xFF
#define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1)
@@ -28,5 +44,6 @@
#define PIDFD_GET_TIME_FOR_CHILDREN_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 8)
#define PIDFD_GET_USER_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 9)
#define PIDFD_GET_UTS_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 10)
+#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
#endif /* _UAPI_LINUX_PIDFD_H */
diff --git a/tools/testing/selftests/pidfd/pidfd_open_test.c b/tools/testing/selftests/pidfd/pidfd_open_test.c
index c62564c264b1..929588c7e0f0 100644
--- a/tools/testing/selftests/pidfd/pidfd_open_test.c
+++ b/tools/testing/selftests/pidfd/pidfd_open_test.c
@@ -13,6 +13,7 @@
#include <stdlib.h>
#include <string.h>
#include <syscall.h>
+#include <sys/ioctl.h>
#include <sys/mount.h>
#include <sys/prctl.h>
#include <sys/wait.h>
@@ -21,6 +22,28 @@
#include "pidfd.h"
#include "../kselftest.h"
+#ifndef PIDFS_IOCTL_MAGIC
+#define PIDFS_IOCTL_MAGIC 0xFF
+#endif
+
+#ifndef PIDFD_GET_INFO
+#define PIDFD_GET_INFO _IOWR(PIDFS_IOCTL_MAGIC, 11, struct pidfd_info)
+#define PIDFD_INFO_PID (1UL << 0)
+#define PIDFD_INFO_CREDS (1UL << 1)
+#define PIDFD_INFO_CGROUPID (1UL << 2)
+#define PIDFD_INFO_SECURITY_CONTEXT (1UL << 3)
+
+struct pidfd_info {
+ __u64 request_mask;
+ __u32 size;
+ uint pid;
+ uint uid;
+ uint gid;
+ __u64 cgroupid;
+ char security_context[NAME_MAX];
+} __attribute__((__packed__));
+#endif
+
static int safe_int(const char *numstr, int *converted)
{
char *err = NULL;
@@ -120,10 +143,14 @@ static pid_t get_pid_from_fdinfo_file(int pidfd, const char *key, size_t keylen)
int main(int argc, char **argv)
{
+ struct pidfd_info info = {
+ .size = sizeof(struct pidfd_info),
+ .request_mask = PIDFD_INFO_PID | PIDFD_INFO_CREDS | PIDFD_INFO_CGROUPID,
+ };
int pidfd = -1, ret = 1;
pid_t pid;
- ksft_set_plan(3);
+ ksft_set_plan(4);
pidfd = sys_pidfd_open(-1, 0);
if (pidfd >= 0) {
@@ -153,6 +180,27 @@ int main(int argc, char **argv)
pid = get_pid_from_fdinfo_file(pidfd, "Pid:", sizeof("Pid:") - 1);
ksft_print_msg("pidfd %d refers to process with pid %d\n", pidfd, pid);
+ if (ioctl(pidfd, PIDFD_GET_INFO, &info) < 0) {
+ ksft_print_msg("%s - failed to get info from pidfd\n", strerror(errno));
+ goto on_error;
+ }
+ if (info.pid != pid) {
+ ksft_print_msg("pid from fdinfo file %d does not match pid from ioctl %d\n",
+ pid, info.pid);
+ goto on_error;
+ }
+ if (info.uid != getuid()) {
+ ksft_print_msg("uid %d does not match uid from info %d\n",
+ getuid(), info.uid);
+ goto on_error;
+ }
+ if (info.gid != getgid()) {
+ ksft_print_msg("gid %d does not match gid from info %d\n",
+ getgid(), info.gid);
+ goto on_error;
+ }
+ ksft_test_result_pass("get info from pidfd test: passed\n");
+
ret = 0;
on_error:
--
2.45.2
next reply other threads:[~2024-10-02 14:25 UTC|newest]
Thread overview: 20+ messages / expand[flat|nested] mbox.gz Atom feed top
2024-10-02 14:24 luca.boccassi [this message]
2024-10-02 14:48 ` Paul Moore
2024-10-04 18:48 ` Luca Boccassi
2024-10-05 16:05 ` Paul Moore
2024-10-22 23:45 ` luca.boccassi
2024-10-22 23:56 ` Luca Boccassi
2024-10-24 23:14 ` Paul Moore
2024-10-24 23:31 ` Luca Boccassi
2024-10-03 21:54 ` kernel test robot
2024-10-03 21:54 ` kernel test robot
2024-10-04 3:55 ` kernel test robot
2024-10-04 9:29 ` Christian Brauner
2024-10-04 14:05 ` Paul Moore
2024-10-04 18:50 ` Luca Boccassi
2024-10-04 19:29 ` Oleg Nesterov
2024-10-04 19:40 ` Luca Boccassi
2024-10-05 11:29 ` Oleg Nesterov
2024-10-06 14:59 ` Luca Boccassi
2024-10-06 19:18 ` David Laight
2024-10-07 14:54 ` Josh Triplett
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20241002142516.110567-1-luca.boccassi@gmail.com \
--to=luca.boccassi@gmail.com \
--cc=christian@brauner.io \
--cc=linux-kernel@vger.kernel.org \
--cc=paul@paul-moore.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®