From nobody Thu Nov 28 02:47:05 2024 Received: from mail-wm1-f49.google.com (mail-wm1-f49.google.com [209.85.128.49]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA7831E0DDA for ; Fri, 4 Oct 2024 19:48:00 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728071282; cv=none; b=efZrFTofxb2FqymC8cudvvx1dt9YEnug10GrRpBf4K2V7Hm+8ytexj4Wbbjylf97BGkJtj6mE7L9S+7FCja19MBiEB2jh8n+/h8GUwq3Cwluv9xnOdPU0wB28Y63s3oJv2Eq1dXe1V8GTebqwTnpql5vOe3J7jAWO3+fw4wMfLo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728071282; c=relaxed/simple; bh=VY/hRY8i9++4TS9FUJxR8tGWUYC/CJStwEAyhlk+Em4=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=lNHmkmLJfgiHwrgicxGXoBa6kG+3QQCbt/AILxSn4AzAt17pFHA0MC8416a9FYwEqeajAOfNGV3YGDoa32THJQHa3raR077YfWOR/jkYpxbX8dcrv/Yb19s9KpNjR1mN0ftxyMALP4sOwrZUHvvPGqfW2x7siFog5i1IljNvUh0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=eZkxIVZu; arc=none smtp.client-ip=209.85.128.49 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="eZkxIVZu" Received: by mail-wm1-f49.google.com with SMTP id 5b1f17b1804b1-42e82f7f36aso21924645e9.0 for ; Fri, 04 Oct 2024 12:48:00 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728071279; x=1728676079; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=7UznPqTvN7fqfKJJRYWFcLZq36QxAU7mZZHzGCL2ja0=; b=eZkxIVZudcQsZ+odGIk0F+RWzgsZwqeoomJBSJwqMfA5yW2G/vE2l703jTA72lhKWa zZRKit3+sYcmV3jxJ5nCdqchtW26PmtAou16CiCq0i4FFLghX34QlNJVshNJ2iJ2kMp9 e5hyFjag4TXdeVq3DVUawg+iGh6s40YWFAtfSVxSP76zagmLv0aOuaDaIEgpLo9YOSjn 65WWMGnu6XE9yyEYqhWbGQrZK3v5/h9WNowRfIcJA2AREfO3PdDJfgCcbwIPP25uDWc8 RHMVWAofgsH9kkKpq8f6m9E0abOkOtIHgVKWYuoD3TcLVr9mQ2XvfTVEBmvfRvHhX3hs aa5Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728071279; x=1728676079; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-message-state:from:to:cc:subject:date:message-id :reply-to; bh=7UznPqTvN7fqfKJJRYWFcLZq36QxAU7mZZHzGCL2ja0=; b=QE7gkNNZn1PEsWMV97bLViQNIt3wveDt2gOs+JfkYJ9go0RWQEMlZOtWWLJcdzyGDJ bNRwV8VcWPUsHt9BUonVbIrMbrTsFq/ahFPb6C0miSzBEudPH2hgosf15kR+wLRfZ6lw kJuCgwp5pL2+yPFPo5O+tZ4AU05u8UGhSMomxgrkdH/jS6g31vbBTpJbZQHHTg2vCjGM wHsAPwG8+vV4HXLJUihKYDqlRJbta6syAoIFSM3SzPUcpuQSUSr3mIjnBiSoJLKwNGqV 9QXTGPmjBpN9teWMVJ4aFiaN2BCgteJ2fp08cMFYh9QmnTt617is3YCHzgbtJGi6ZWnb CiQw== X-Gm-Message-State: AOJu0Ywd6jyhsAbcSst+PZLCK5efLMerlAmc0scWD7oSvh0+vgVHXVXD ARfR1NoWSUzkqsZp+vPRMWky/wpCv/xfeUTCCj82A+EHsgPynBHPxGT04Q== X-Google-Smtp-Source: AGHT+IFpnQAIJjL0w+k/3Ji8xBHfaLwd1U1g6lJBNDs19YI0NW67s4CTS4DLKdxJVEAdSAExQDmImg== X-Received: by 2002:a05:600c:350c:b0:42c:b750:19ce with SMTP id 5b1f17b1804b1-42f85aa33bemr28276615e9.1.1728071278383; Fri, 04 Oct 2024 12:47:58 -0700 (PDT) Received: from localhost ([2a01:4b00:d036:ae00:a3f4:1a10:8066:7b14]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42f86b445d2sm22665945e9.36.2024.10.04.12.47.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Oct 2024 12:47:57 -0700 (PDT) From: luca.boccassi@gmail.com To: linux-kernel@vger.kernel.org Cc: christian@brauner.io Subject: [PATCH v4] pidfd: add ioctl to retrieve pid info Date: Fri, 4 Oct 2024 20:47:03 +0100 Message-ID: <20241004194751.215507-1-luca.boccassi@gmail.com> X-Mailer: git-send-email 2.45.2 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" From: Luca Boccassi 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 returning pid/tgid/ppid and creds unconditionally, and cgroupid optionally. Signed-off-by: Luca Boccassi --- v4: fix arg check in pidfd_ioctl() by moving it after the new call v3: switch from pid_vnr() to task_pid_vnr() v2: Apply comments from Christian, apart from the one about pid namespaces as I need additional hints on how to implement it. Drop the security_context string as it is not the appropriate metadata to give userspace these days. fs/pidfs.c | 70 +++++++++++++++- include/uapi/linux/pidfd.h | 24 ++++++ .../testing/selftests/pidfd/pidfd_open_test.c | 81 ++++++++++++++++++- 3 files changed, 171 insertions(+), 4 deletions(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index 7ffdc88dfb52..9ef63c1053d4 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include #include @@ -114,6 +115,65 @@ static __poll_t pidfd_poll(struct file *file, struct p= oll_table_struct *pts) return poll_flags; } =20 +static long pidfd_info(struct task_struct *task, unsigned int cmd, unsigne= d long arg) +{ + struct pidfd_info __user *uinfo =3D (struct pidfd_info __user *)arg; + size_t usize =3D _IOC_SIZE(cmd); + struct pidfd_info kinfo =3D {}; + struct user_namespace *user_ns; + const struct cred *c; + __u64 request_mask; + + if (!uinfo) + return -EINVAL; + if (usize < sizeof(struct pidfd_info)) + return -EINVAL; /* First version, no smaller struct possible */ + + if (copy_from_user(&request_mask, &uinfo->request_mask, sizeof(request_ma= sk))) + return -EFAULT; + + c =3D get_task_cred(task); + if (!c) + return -ESRCH; + + /* Unconditionally return identifiers and credentials, the rest only on r= equest */ + + kinfo.pid =3D task_pid_vnr(task); + kinfo.tgid =3D task_tgid_vnr(task); + kinfo.ppid =3D task_ppid_nr_ns(task, task_active_pid_ns(task)); + + user_ns =3D current_user_ns(); + kinfo.ruid =3D from_kuid_munged(user_ns, c->uid); + kinfo.rgid =3D from_kgid_munged(user_ns, c->gid); + kinfo.euid =3D from_kuid_munged(user_ns, c->euid); + kinfo.egid =3D from_kgid_munged(user_ns, c->egid); + kinfo.suid =3D from_kuid_munged(user_ns, c->suid); + kinfo.sgid =3D from_kgid_munged(user_ns, c->sgid); + kinfo.fsuid =3D from_kuid_munged(user_ns, c->fsuid); + kinfo.fsgid =3D from_kgid_munged(user_ns, c->fsgid); + + if (request_mask & PIDFD_INFO_CGROUPID) { + struct cgroup *cgrp =3D task_css_check(task, pids_cgrp_id, 1)->cgroup; + if (!cgrp) + return -ENODEV; + + kinfo.cgroupid =3D cgroup_id(cgrp); + kinfo.result_mask |=3D PIDFD_INFO_CGROUPID; + } + + /* + * If userspace and the kernel have the same struct size it can just + * be copied. If userspace provides an older struct, only the bits that + * userspace knows about will be copied. If userspace provides a new + * struct, only the bits that the kernel knows about will be copied and + * the size value will be set to the size the kernel knows about. + */ + if (copy_to_user(uinfo, &kinfo, min(usize, sizeof(kinfo)))) + return -EFAULT; + + return 0; +} + static long pidfd_ioctl(struct file *file, unsigned int cmd, unsigned long= arg) { struct task_struct *task __free(put_task) =3D NULL; @@ -121,13 +181,17 @@ static long pidfd_ioctl(struct file *file, unsigned i= nt cmd, unsigned long arg) struct pid *pid =3D pidfd_pid(file); struct ns_common *ns_common =3D NULL; =20 - if (arg) - return -EINVAL; - task =3D get_pid_task(pid, PIDTYPE_PID); if (!task) return -ESRCH; =20 + /* Extensible IOCTL that does not open namespace FDs, take a shortcut */ + if (_IOC_NR(cmd) =3D=3D _IOC_NR(PIDFD_GET_INFO)) + return pidfd_info(task, cmd, arg); + + if (arg) + return -EINVAL; + scoped_guard(task_lock, task) { nsp =3D task->nsproxy; if (nsp) diff --git a/include/uapi/linux/pidfd.h b/include/uapi/linux/pidfd.h index 565fc0629fff..f278db1a3fe8 100644 --- a/include/uapi/linux/pidfd.h +++ b/include/uapi/linux/pidfd.h @@ -16,6 +16,29 @@ #define PIDFD_SIGNAL_THREAD_GROUP (1UL << 1) #define PIDFD_SIGNAL_PROCESS_GROUP (1UL << 2) =20 +/* Flags for pidfd_info. */ +#define PIDFD_INFO_CGROUPID (1UL << 0) + +struct pidfd_info { + /* Let userspace request expensive stuff explictly. */ + __u64 request_mask; + /* And let the kernel indicate whether it knows about it. */ + __u64 result_mask; + __u64 cgroupid; + __u32 pid; + __u32 tgid; + __u32 ppid; + __u32 ruid; + __u32 rgid; + __u32 euid; + __u32 egid; + __u32 suid; + __u32 sgid; + __u32 fsuid; + __u32 fsgid; + __u32 spare0[1]; +}; + #define PIDFS_IOCTL_MAGIC 0xFF =20 #define PIDFD_GET_CGROUP_NAMESPACE _IO(PIDFS_IOCTL_MAGIC, 1) @@ -28,5 +51,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) =20 #endif /* _UAPI_LINUX_PIDFD_H */ diff --git a/tools/testing/selftests/pidfd/pidfd_open_test.c b/tools/testin= g/selftests/pidfd/pidfd_open_test.c index c62564c264b1..30c50a8ae10b 100644 --- a/tools/testing/selftests/pidfd/pidfd_open_test.c +++ b/tools/testing/selftests/pidfd/pidfd_open_test.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -21,6 +22,35 @@ #include "pidfd.h" #include "../kselftest.h" =20 +#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_CGROUPID (1UL << 0) + +struct pidfd_info { + /* Let userspace request expensive stuff explictly. */ + __u64 request_mask; + /* And let the kernel indicate whether it knows about it. */ + __u64 result_mask; + __u64 cgroupid; + __u32 pid; + __u32 tgid; + __u32 ppid; + __u32 ruid; + __u32 rgid; + __u32 euid; + __u32 egid; + __u32 suid; + __u32 sgid; + __u32 fsuid; + __u32 fsgid; + __u32 spare0[1]; +}; +#endif + static int safe_int(const char *numstr, int *converted) { char *err =3D NULL; @@ -120,10 +150,13 @@ static pid_t get_pid_from_fdinfo_file(int pidfd, cons= t char *key, size_t keylen) =20 int main(int argc, char **argv) { + struct pidfd_info info =3D { + .request_mask =3D PIDFD_INFO_CGROUPID, + }; int pidfd =3D -1, ret =3D 1; pid_t pid; =20 - ksft_set_plan(3); + ksft_set_plan(4); =20 pidfd =3D sys_pidfd_open(-1, 0); if (pidfd >=3D 0) { @@ -153,6 +186,52 @@ int main(int argc, char **argv) pid =3D get_pid_from_fdinfo_file(pidfd, "Pid:", sizeof("Pid:") - 1); ksft_print_msg("pidfd %d refers to process with pid %d\n", pidfd, pid); =20 + 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 !=3D 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.ppid !=3D getppid()) { + ksft_print_msg("ppid %d does not match ppid from ioctl %d\n", + pid, info.pid); + goto on_error; + } + if (info.ruid !=3D getuid()) { + ksft_print_msg("uid %d does not match uid from ioctl %d\n", + getuid(), info.ruid); + goto on_error; + } + if (info.rgid !=3D getgid()) { + ksft_print_msg("gid %d does not match gid from ioctl %d\n", + getgid(), info.rgid); + goto on_error; + } + if (info.euid !=3D geteuid()) { + ksft_print_msg("euid %d does not match euid from ioctl %d\n", + geteuid(), info.euid); + goto on_error; + } + if (info.egid !=3D getegid()) { + ksft_print_msg("egid %d does not match egid from ioctl %d\n", + getegid(), info.egid); + goto on_error; + } + if (info.suid !=3D geteuid()) { + ksft_print_msg("suid %d does not match suid from ioctl %d\n", + geteuid(), info.suid); + goto on_error; + } + if (info.sgid !=3D getegid()) { + ksft_print_msg("sgid %d does not match sgid from ioctl %d\n", + getegid(), info.sgid); + goto on_error; + } + ksft_test_result_pass("get info from pidfd test: passed\n"); + ret =3D 0; =20 on_error: base-commit: 9852d85ec9d492ebef56dc5f229416c925758edc --=20 2.45.2