pidfs: do not report fields that do not fit into the caller's struct

The documentation of struct pidfd_info promises that "If the structure
size is too small to contain a field (requested or not), to avoid
confusion the mask will not contain a bit for that field", and asks
userspace to trust the returned mask when deciding which fields hold
valid data.

pidfd_info() does not keep that promise. It builds kinfo.mask without
looking at the size encoded in the ioctl command, and
copy_struct_to_user() then truncates the copy to that size. A caller
that passes a 64-byte struct (PIDFD_INFO_SIZE_VER0) and requests
PIDFD_INFO_COREDUMP or PIDFD_INFO_SUPPORTED_MASK gets a mask with
PIDFD_INFO_COREDUMP, PIDFD_INFO_COREDUMP_SIGNAL, PIDFD_INFO_COREDUMP_CODE
and PIDFD_INFO_SUPPORTED_MASK set, while the coredump_* and
supported_mask fields behind the 64 bytes were never written. Trusting
the mask as documented, userspace then reads whatever its own buffer
held at those offsets. The same happens for a 72- or 80-byte struct
and the fields added after it.

Clear the bits of the fields that lie beyond the caller's size before
the copy, in the one place that knows both the final mask and the size.
A request for a field that cannot be returned is thereby answered the
way the header describes: the field is simply absent from the mask.

Fixes: 1d8db6fd69 ("pidfs, coredump: add PIDFD_INFO_COREDUMP")
Signed-off-by: Nickolai Zeldovich <nickolai@csail.mit.edu>
Link: https://patch.msgid.link/20261024041334.831273-2-nickolai@csail.mit.edu
Acked-by: Luca Boccassi <luca.boccassi@gmail.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Nickolai Zeldovich 2026-10-24 00:13:33 -04:00 committed by Christian Brauner
parent c3d355f427
commit 1534c37167
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -511,6 +511,14 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg)
kinfo.supported_mask = PIDFD_INFO_SUPPORTED;
}
/* Only report fields that fit into the caller's struct. */
if (usize < PIDFD_INFO_SIZE_VER1)
kinfo.mask &= ~(PIDFD_INFO_COREDUMP | PIDFD_INFO_COREDUMP_SIGNAL);
if (usize < PIDFD_INFO_SIZE_VER2)
kinfo.mask &= ~PIDFD_INFO_COREDUMP_CODE;
if (usize < PIDFD_INFO_SIZE_VER3)
kinfo.mask &= ~PIDFD_INFO_SUPPORTED_MASK;
/* Are there bits in the return mask not present in PIDFD_INFO_SUPPORTED? */
WARN_ON_ONCE(~PIDFD_INFO_SUPPORTED & kinfo.mask);
/*