[PATCH 3/3] pidfs: don't report pidfd_info fields that won't fit in the user buffer

From: Christian Brauner

Date: Mon Apr 20 2026 - 12:18:53 EST


The UAPI documentation for struct pidfd_info promises that if the
structure provided by userspace is too small to contain a field, the
kernel will not set the corresponding bit in the returned mask.

The kernel violates this contract: it sets PIDFD_INFO_COREDUMP and
PIDFD_INFO_COREDUMP_SIGNAL in the returned mask without checking that
usize >= PIDFD_INFO_SIZE_VER1 (the coredump fields start at offset 64,
beyond a VER0 buffer). Similarly, PIDFD_INFO_SUPPORTED_MASK is set
without checking usize >= PIDFD_INFO_SIZE_VER2.

While copy_struct_to_user() correctly only copies min(usize, ksize)
bytes (so no kernel memory leaks), userspace that trusts the mask bits
as documented may read its own uninitialized buffer and interpret it as
valid data.

Gate each set of mask bits on the user-provided struct being large
enough to actually deliver the corresponding fields.

Fixes: 9e77e4882bae ("pidfs: support retrieving supported pidfd_info flags")
Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>
---
fs/pidfs.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/fs/pidfs.c b/fs/pidfs.c
index 2ab8fd2646f0..4c24d2eb7e41 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -375,7 +375,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
}
}

- if (mask & PIDFD_INFO_COREDUMP) {
+ if ((mask & PIDFD_INFO_COREDUMP) && usize >= PIDFD_INFO_SIZE_VER1) {
if (test_bit(PIDFS_ATTR_BIT_COREDUMP, &attr->attr_mask)) {
smp_rmb();
kinfo.mask |= PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL;
@@ -400,7 +400,8 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
if (!c)
return -ESRCH;

- if ((mask & PIDFD_INFO_COREDUMP) && !kinfo.coredump_mask) {
+ if ((mask & PIDFD_INFO_COREDUMP) && usize >= PIDFD_INFO_SIZE_VER1 &&
+ !kinfo.coredump_mask) {
guard(task_lock)(task);
if (task->mm) {
unsigned long flags = __mm_flags_get_dumpable(task->mm);
@@ -455,7 +456,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
return -ESRCH;

copy_out:
- if (mask & PIDFD_INFO_SUPPORTED_MASK) {
+ if ((mask & PIDFD_INFO_SUPPORTED_MASK) && usize >= PIDFD_INFO_SIZE_VER2) {
kinfo.mask |= PIDFD_INFO_SUPPORTED_MASK;
kinfo.supported_mask = PIDFD_INFO_SUPPORTED;
}

--
2.47.3