From nobody Thu Nov 28 02:59:31 2024 Received: from mail-wr1-f49.google.com (mail-wr1-f49.google.com [209.85.221.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 6F0B41DF755 for ; Fri, 4 Oct 2024 18:46:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.49 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728067566; cv=none; b=W9pNMFkumrHTPXbM+yN/lUNHXaMLe69Ij8caVo3L/FPLv9czXkIBfnB5UxQOqCk/E8J9Dj1hzMasxXAfsgwoaQW+OsYF70lW5eTUvXcMWfANI3nDvD16Zz0nG3Av3jk6KvAKofEmVUtPrda54ne19KMjhN4Femc3yyAd46zXSTY= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1728067566; c=relaxed/simple; bh=0U1/1g0EKTnE37iX902u87Apayf6pj50FjdE3I+n7CA=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=JeWEejnJTxZNYUlCx5FLv7SwFKBIkiOie1x8V9wsGauL31lNqilE04SCYx6ouEr0Y4YQ+XuIR7eiispe0atPuzyq57E3TYz4FuvJFPEscciEKBuH+Yk6/8iPNefcR1RPTbcjkXOjmxtF3grD/I04niub0YnI5fQ3sf62QjjMy3w= 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=IzaCv4YR; arc=none smtp.client-ip=209.85.221.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="IzaCv4YR" Received: by mail-wr1-f49.google.com with SMTP id ffacd0b85a97d-37d04b32ea6so1660340f8f.1 for ; Fri, 04 Oct 2024 11:46:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1728067562; x=1728672362; 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=XFR+SBGiFYLUkzJl8vcZMMqSya9u93y0vE7d0eDlf+4=; b=IzaCv4YRFmltuX+kvMvSUi/w6sl/xxN4F2+87pTZOUT7rqwi2scg6dBAC6yi7NoUNu qaANpahmC1PURZ3lDvzWLCqDmKUL3VBAq7VcKetBLhVy48NMQ+DIDP2mfgBG/fgz9tQR W3peR6nuVo0mamnrELuZIF+HZxdr2ATdzx7YEdyMflthboDHy2Jnoj6y8omqw54lFDnH rFed3ZVcaLs8FXxOCOFk/HkBr2wGkZ4OEBFESbLqKe425tF2LN40sdTqhKJc2W0FxUrb OrzfVuG3DkXKpB1Q+S3HL9VvvBN7gZuDGGxroyKRL4wwDDor+czqj7ahGHFM6Etj1Git S8oA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1728067562; x=1728672362; 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=XFR+SBGiFYLUkzJl8vcZMMqSya9u93y0vE7d0eDlf+4=; b=ZbvSZP1LWmKPaY3QDfNMzU6ojLz0k+x3gQUjoOeXen+aWQ5frD2je6fdiSq5O+z79i VYcXErUIYLBeXfW79K9ddseVtak0tbEUVSyiJ6efJPAXABgboroQwlfFuwdMNQduokE1 M1wcPvDmBMQNvQBW/QJKo/OplOjwu2yAbma7JvSaPiu663MGvSSnr3/XO9nePqLtMIBc lUnmBea3HnQDBJCMlsPyVwt5TF7Vt3+zer58Val00Oxl20GVOlHTUcCwfkzgJi3dC4kX v8Gw1mQk8J4aGN5ATnGOpJlNE99jaiVln/uHpT6etSHBOngdpEG5+kWE761uUfkezNXO vGEg== X-Gm-Message-State: AOJu0Yw8NCU7oDRNY2qRcw1XBP+P1Ixzxry1BxV4VJpB0gmBhxAJgG68 4az5oWE3xTb5atqQg9+6l1o/9dqC0SOKQZudd0IRbTqS8DV5A1LtIm08GQ== X-Google-Smtp-Source: AGHT+IGtH7sw1AvXjHjTTBrD5THR6buuoGMDdingnVuc8jEEFLIVfPIVgWKEg1qmVu0S8N1N0/SQ+g== X-Received: by 2002:a5d:598c:0:b0:374:fa0a:773c with SMTP id ffacd0b85a97d-37d0eafa3admr2351297f8f.47.1728067562099; Fri, 04 Oct 2024 11:46:02 -0700 (PDT) Received: from localhost ([2a01:4b00:d036:ae00:a3f4:1a10:8066:7b14]) by smtp.gmail.com with ESMTPSA id 5b1f17b1804b1-42f89ed91b8sm3738565e9.41.2024.10.04.11.46.00 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 04 Oct 2024 11:46:01 -0700 (PDT) From: luca.boccassi@gmail.com To: linux-kernel@vger.kernel.org Cc: christian@brauner.io Subject: [PATCH v2] pidfd: add ioctl to retrieve pid info Date: Fri, 4 Oct 2024 19:43:08 +0100 Message-ID: <20241004184555.193040-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 --- 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=20 metadata to give userspace these days. fs/pidfs.c | 66 ++++++++++++++- include/uapi/linux/pidfd.h | 24 ++++++ .../testing/selftests/pidfd/pidfd_open_test.c | 81 ++++++++++++++++++- 3 files changed, 169 insertions(+), 2 deletions(-) diff --git a/fs/pidfs.c b/fs/pidfs.c index 7ffdc88dfb52..c13aa2660630 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, struct pid *pid, unsigned= int cmd, unsigned 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 pid_vnr(pid); + 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) + if (!!arg !=3D (cmd =3D=3D PIDFD_GET_INFO)) return -EINVAL; =20 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, pid, cmd, arg); + 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