[PATCH RFC v4 0/2] pidfs: make the {g,u}id the owner of the inode

From: Christian Brauner

Date: Wed Feb 25 2026 - 18:23:07 EST


This adds inode ownership and permission checking to pidfs.

Right now pidfs only supports trusted.* xattrs which require
CAP_SYS_ADMIN so there was never a need for real permission checking.
In order to support user.* xattrs and custom pidfs.* xattrs in the
future we need a permission model for pidfs inodes.

The {u,g}id of the target task becomes the owner of the pidfs inode.
Ownership is reported dynamically via getattr since credentials may
change due to setresuid() and similar operations. For kernel threads the
owner is root, for exited tasks the credentials saved at exit time via
pidfs_exit() are used.

The permission callback updates the inode ownership via
pidfs_update_owner() and then performs standard POSIX permission
checking via generic_permission().

This is intentionally less strict than ptrace_may_access() because pidfs
currently does not allow operating on data that is completely private to
the process such as its mm or file descriptors. Additional checks can be
layered on once that changes.

The second patch adds selftests covering ownership reporting via fstat
and the permission model via user.* xattr operations which trigger
pidfs_permission() through xattr_permission(). The tests exercise live
credential changes, exited tasks with saved exit credentials, same-user
cross-process access, and cross-user denial.

Signed-off-by: Christian Brauner <brauner@xxxxxxxxxx>
---
Changes in v4:
- Switch from euid/egid to uid/gid for inode ownership per Jann Horn's
feedback. Use cred->uid/cred->gid instead of cred->euid/cred->egid.
- Simplify pidfs_permission() to just call pidfs_update_owner() followed
by generic_permission() instead of open-coding credential checks.
- Drop the may_signal_creds() helper and the kill_ok_by_cred() changes
that were part of v3's two-step permission model.
- Fix bitmask enum values: use BIT(N) instead of plain integers since
attr_mask is now an atomic_t using atomic_or()/atomic_read() instead
of set_bit()/test_bit().
- Make PIDFS_ATTR_BIT_KTHREAD conditional on PF_KTHREAD in pidfs_exit()
instead of unconditionally setting it for all exiting tasks.
- Remove unused kuid_t/kgid_t variables from pidfs_update_owner().
- Link to v3: https://patch.msgid.link/20260223-work-pidfs-inode-owner-v3-0-490855c59999@xxxxxxxxxx

Changes in v3:
- Simplify pidfs_fill_owner() into pidfs_update_owner() writing directly
to the inode via WRITE_ONCE() instead of using output parameters.
- Drop the separate pidfs_update_inode() helper and the
security_task_to_inode() call.
- Update pidfs_getattr() to write ownership to the inode via
pidfs_update_owner() instead of writing directly to stat.
- Update pidfs_permission() to also write ownership to the inode before
calling generic_permission(), handling kernel threads with -EPERM.
- Drop VFS_WARN_ON_ONCE() for idmap check from pidfs_permission().
- Link to v2: https://patch.msgid.link/20260217-work-pidfs-inode-owner-v2-1-f04b5638315a@xxxxxxxxxx

Changes in v2:
- Fix an obvious null-deref during PIDFD_STALE (CLONE_PIDFD).
- Link to v1: https://patch.msgid.link/20260216-work-pidfs-inode-owner-v1-1-f8faa6b73983@xxxxxxxxxx

---
Christian Brauner (2):
pidfs: add inode ownership and permission checks
selftests/pidfd: add inode ownership and permission tests

fs/pidfs.c | 110 ++++++--
tools/testing/selftests/pidfd/.gitignore | 1 +
tools/testing/selftests/pidfd/Makefile | 2 +-
.../selftests/pidfd/pidfd_inode_owner_test.c | 314 +++++++++++++++++++++
4 files changed, 409 insertions(+), 18 deletions(-)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20260211-work-pidfs-inode-owner-0ca20de9ef23