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 <[email protected]>
---
 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


Reply via email to