[RFC PATCH 08/24] pidfs: attach pids to future pidfd files
From: Li Chen
Date: Thu Jul 16 2026 - 11:51:22 EST
Associate a future pidfs inode with a preallocated struct pid and reuse
the future dentry as the canonical stashed dentry. Preserve the
reserved inode identity when the pid is registered.
Keep attachment reversible until a task owns the pid so copy_process()
failure can return the future file to its taskless state. File-handle
lookup requires both the stash and an attached task before accepting an
unpublished pid, preventing failed allocations from being reopened
through stale state.
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Li Chen <me@linux.beauty>
---
fs/pidfs.c | 89 +++++++++++++++++++++++++++++++++++++++++--
include/linux/pidfs.h | 2 +
2 files changed, 87 insertions(+), 4 deletions(-)
diff --git a/fs/pidfs.c b/fs/pidfs.c
index 28464fe274c9e..36ee4f210f73f 100644
--- a/fs/pidfs.c
+++ b/fs/pidfs.c
@@ -115,6 +115,7 @@ struct pidfs_future_file {
void *data;
const struct pidfs_future_file_ops *ops;
struct pidfs_attr *attr;
+ struct pid *pid;
u64 ino;
};
@@ -238,7 +239,8 @@ int pidfs_add_pid(struct pid *pid)
{
int ret;
- pid->ino = pidfs_alloc_ino();
+ if (!pid->ino)
+ pid->ino = pidfs_alloc_ino();
ret = rhashtable_insert_fast(&pidfs_ino_ht, &pid->pidfs_hash,
pidfs_ino_ht_params);
if (unlikely(ret))
@@ -987,9 +989,18 @@ static struct pid *pidfs_ino_get_pid(u64 ino)
if (!pid)
return NULL;
attr = READ_ONCE(pid->attr);
- if (IS_ERR_OR_NULL(attr))
+ if (IS_ERR(attr))
+ return NULL;
+ /*
+ * A task can become visible before its future pidfd is published. Let
+ * ->open() wait across that window, but never expose a dentry for a
+ * caller-supplied pid when copy_process() has failed. That failure path
+ * clears the stash before freeing the pid allocation.
+ */
+ if (!attr && (!READ_ONCE(pid->stashed) ||
+ !pid_has_task(pid, PIDTYPE_PID)))
return NULL;
- if (test_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask))
+ if (attr && test_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask))
return NULL;
/* Within our pid namespace hierarchy? */
if (pid_vnr(pid) == 0)
@@ -1025,7 +1036,8 @@ static struct dentry *pidfs_fh_to_dentry(struct super_block *sb,
if (ret < 0)
return ERR_PTR(ret);
- VFS_WARN_ON_ONCE(!pid->attr);
+ VFS_WARN_ON_ONCE(!pid->attr &&
+ !pidfs_future_file(d_inode(path.dentry)));
mntput(path.mnt);
return path.dentry;
@@ -1103,6 +1115,10 @@ static int pidfs_init_inode(struct inode *inode, void *data)
static bool pidfs_inode_data_matches(const struct inode *inode,
const void *data)
{
+ struct pidfs_future_file *future = pidfs_future_file(inode);
+
+ if (future)
+ return READ_ONCE(future->pid) == data;
return pidfs_inode_pid(inode) == data;
}
@@ -1266,6 +1282,10 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags)
PIDFD_AUTOKILL) != 5);
BUILD_BUG_ON(PIDFD_EMPTY & VALID_OPEN_FLAGS);
+ ret = pidfs_register_pid(pid);
+ if (ret)
+ return ERR_PTR(ret);
+
ret = path_from_stashed(&pid->stashed, pidfs_mnt, get_pid(pid), &path);
if (ret < 0)
return ERR_PTR(ret);
@@ -1369,6 +1389,67 @@ u64 pidfs_future_file_ino(const struct file *file)
return future ? future->ino : 0;
}
+/**
+ * pidfs_future_file_set_pid - associate a preallocated pid with a future file
+ * @file: future pidfs file
+ * @pid: preallocated pid whose pidfs inode number matches @file
+ *
+ * This stashes the future dentry in @pid before copy_process() publishes the
+ * task. No pid reference is transferred. On a later creation failure the
+ * producer must call pidfs_future_file_clear_pid() before freeing @pid.
+ *
+ * Return: Zero on success or a negative error code.
+ */
+int pidfs_future_file_set_pid(struct file *file, struct pid *pid)
+{
+ struct inode *inode = file_inode(file);
+ struct pidfs_future_file *future = pidfs_future_file(inode);
+ struct dentry *dentry = file->f_path.dentry;
+ struct dentry *stashed;
+
+ if (!future || !future->ops->get_pid || future->pid ||
+ pid->ino != future->ino)
+ return -EBADF;
+
+ WRITE_ONCE(future->pid, pid);
+ dentry->d_fsdata = &pid->stashed;
+ stashed = stash_dentry(&pid->stashed, dentry);
+ if (stashed != dentry) {
+ dput(stashed);
+ pidfs_future_file_clear_pid(file, pid);
+ return -EBUSY;
+ }
+ return 0;
+}
+
+/**
+ * pidfs_future_file_clear_pid - undo an unpublished future-pid association
+ * @file: future pidfs file
+ * @pid: pid previously associated with @file
+ *
+ * This may be called only after pidfs_future_file_set_pid() and before
+ * pidfs_future_file_publish_pid(). It removes the stashed dentry so the
+ * producer can free @pid after a task-creation failure.
+ *
+ * The producer must clear the association before attaching @pid to a task.
+ * File-handle lookup treats an attached pid as a publication-in-progress
+ * guarantee, so clearing the association later would violate that contract.
+ */
+void pidfs_future_file_clear_pid(struct file *file, struct pid *pid)
+{
+ struct inode *inode = file_inode(file);
+ struct pidfs_future_file *future = pidfs_future_file(inode);
+ struct dentry *dentry = file->f_path.dentry;
+
+ if (WARN_ON_ONCE(!future || READ_ONCE(future->pid) != pid))
+ return;
+
+ WARN_ON_ONCE(cmpxchg(&pid->stashed, dentry, NULL) != dentry);
+ dentry->d_fsdata = NULL;
+ WARN_ON_ONCE(pid->attr);
+ WRITE_ONCE(future->pid, NULL);
+}
+
void __init pidfs_init(void)
{
if (rhashtable_init(&pidfs_ino_ht, &pidfs_ino_ht_params))
diff --git a/include/linux/pidfs.h b/include/linux/pidfs.h
index 6b7fbc54ab388..828e96f769ba1 100644
--- a/include/linux/pidfs.h
+++ b/include/linux/pidfs.h
@@ -28,6 +28,8 @@ struct file *pidfs_alloc_future_file(const char *name, void *data,
void *pidfs_future_file_data(const struct file *file,
const struct pidfs_future_file_ops *ops);
u64 pidfs_future_file_ino(const struct file *file);
+int pidfs_future_file_set_pid(struct file *file, struct pid *pid);
+void pidfs_future_file_clear_pid(struct file *file, struct pid *pid);
void __init pidfs_init(void);
void pidfs_prepare_pid(struct pid *pid);
int pidfs_add_pid(struct pid *pid);
--
2.52.0