Thread (26 messages) 26 messages, 2 authors, 3d ago
WARM3d

[RFC PATCH 08/24] pidfs: attach pids to future pidfd files

From: Li Chen <hidden>
Date: 2026-07-16 14:37:23
Also in: linux-arch, linux-doc, linux-fsdevel, linux-kselftest, linux-mm, linux-security-module, lkml
Subsystem: filesystems (vfs and infrastructure), the rest · Maintainers: Alexander Viro, Christian Brauner, Linus Torvalds

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 <redacted>
---
 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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help