Merge patch series "pidfs: persistent info & xattrs"
authorChristian Brauner <brauner@kernel.org>
Thu, 19 Jun 2025 12:28:31 +0000 (14:28 +0200)
committerChristian Brauner <brauner@kernel.org>
Mon, 23 Jun 2025 10:22:09 +0000 (12:22 +0200)
commit4e3d1e6e1b2d9df9650be14380c534b3c5081ddd
tree3def5fe12a4ec2c905f11546fa49768851fc73d1
parent19272b37aa4f83ca52bdf9c16d5d81bdd1354494
parentf9fac1f48c20a29f2c39c9a9b96d539ad1636824
Merge patch series "pidfs: persistent info & xattrs"

Christian Brauner <brauner@kernel.org> says:

Persist exit and coredump information independent of whether anyone
currently holds a pidfd for the struct pid.

The current scheme allocated pidfs dentries on-demand repeatedly.
This scheme is reaching it's limits as it makes it impossible to pin
information that needs to be available after the task has exited or
coredumped and that should not be lost simply because the pidfd got
closed temporarily. The next opener should still see the stashed
information.

This is also a prerequisite for supporting extended attributes on
pidfds to allow attaching meta information to them.

If someone opens a pidfd for a struct pid a pidfs dentry is allocated
and stashed in pid->stashed. Once the last pidfd for the struct pid is
closed the pidfs dentry is released and removed from pid->stashed.

So if 10 callers create a pidfs dentry for the same struct pid
sequentially, i.e., each closing the pidfd before the other creates a
new one then a new pidfs dentry is allocated every time.

Because multiple tasks acquiring and releasing a pidfd for the same
struct pid can race with each another a task may still find a valid
pidfs entry from the previous task in pid->stashed and reuse it. Or it
might find a dead dentry in there and fail to reuse it and so stashes a
new pidfs dentry. Multiple tasks may race to stash a new pidfs dentry
but only one will succeed, the other ones will put their dentry.

The current scheme aims to ensure that a pidfs dentry for a struct pid
can only be created if the task is still alive or if a pidfs dentry
already existed before the task was reaped and so exit information has
been was stashed in the pidfs inode.

That's great except that it's buggy. If a pidfs dentry is stashed in
pid->stashed after pidfs_exit() but before __unhash_process() is called
we will return a pidfd for a reaped task without exit information being
available.

The pidfds_pid_valid() check does not guard against this race as it
doens't sync at all with pidfs_exit(). The pid_has_task() check might be
successful simply because we're before __unhash_process() but after
pidfs_exit().

Introduce a new scheme where the lifetime of information associated with
a pidfs entry (coredump and exit information) isn't bound to the
lifetime of the pidfs inode but the struct pid itself.

The first time a pidfs dentry is allocated for a struct pid a struct
pidfs_attr will be allocated which will be used to store exit and
coredump information.

If all pidfs for the pidfs dentry are closed the dentry and inode can be
cleaned up but the struct pidfs_attr will stick until the struct pid
itself is freed. This will ensure minimal memory usage while persisting
relevant information.

The new scheme has various advantages. First, it allows to close the
race where we end up handing out a pidfd for a reaped task for which no
exit information is available. Second, it minimizes memory usage.
Third, it allows to remove complex lifetime tracking via dentries when
registering a struct pid with pidfs. There's no need to get or put a
reference. Instead, the lifetime of exit and coredump information
associated with a struct pid is bound to the lifetime of struct pid
itself.

Now that we have a way to persist information for pidfs dentries we can
start supporting extended attributes on pidfds. This will allow
userspace to attach meta information to tasks.

One natural extension would be to introduce a custom pidfs.* extended
attribute space and allow for the inheritance of extended attributes
across fork() and exec().

The first simple scheme will allow privileged userspace to set trusted
extended attributes on pidfs inodes.

* patches from https://lore.kernel.org/20250618-work-pidfs-persistent-v2-0-98f3456fd552@kernel.org:
  pidfs: add some CONFIG_DEBUG_VFS asserts
  selftests/pidfd: test setattr support
  selftests/pidfd: test extended attribute support
  selftests/pidfd: test extended attribute support
  pidfs: support xattrs on pidfds
  pidfs: make inodes mutable
  libfs: prepare to allow for non-immutable pidfd inodes
  pidfs: remove pidfs_pid_valid()
  pidfs: remove pidfs_{get,put}_pid()
  pidfs: remove custom inode allocation
  pidfs: remove unused members from struct pidfs_inode
  pidfs: persist information
  pidfs: move to anonymous struct
  libfs: massage path_from_stashed()
  libfs: massage path_from_stashed() to allow custom stashing behavior
  pidfs: raise SB_I_NODEV and SB_I_NOEXEC

Link: https://lore.kernel.org/20250618-work-pidfs-persistent-v2-0-98f3456fd552@kernel.org
Signed-off-by: Christian Brauner <brauner@kernel.org>