[RFC PATCH 09/24] fork: let process builders supply preallocated pids
From: Li Chen
Date: Thu Jul 16 2026 - 11:03:53 EST
Process builders can reserve a pidfs identity before copy_process()
makes a task visible. Add an alloc_pid() variant for a reserved pidfs
inode, and let copy_process() consume a caller-provided struct pid on
success.
Keep caller ownership on failure. The legacy NULL-pid path continues to
allocate and free its own pid. Validate the target PID namespace before
using a preallocated identity.
Assisted-by: Codex:gpt-5.6-sol
Signed-off-by: Li Chen <me@linux.beauty>
---
include/linux/pid.h | 3 +++
kernel/fork.c | 16 ++++++++++++++--
kernel/pid.c | 22 ++++++++++++++++++++--
3 files changed, 37 insertions(+), 4 deletions(-)
diff --git a/include/linux/pid.h b/include/linux/pid.h
index a29ffe2a5fa8e..fa8acae336f7c 100644
--- a/include/linux/pid.h
+++ b/include/linux/pid.h
@@ -142,6 +142,9 @@ extern struct pid *find_ge_pid(int nr, struct pid_namespace *);
extern struct pid *alloc_pid(struct pid_namespace *ns, pid_t *set_tid,
size_t set_tid_size);
+struct pid *alloc_pid_with_pidfs_ino(struct pid_namespace *ns,
+ pid_t *set_tid, size_t set_tid_size,
+ u64 pidfs_ino);
extern void free_pid(struct pid *pid);
void free_pids(struct pid **pids);
extern void disable_pid_allocation(struct pid_namespace *ns);
diff --git a/kernel/fork.c b/kernel/fork.c
index 970810a01bbf6..d16405c037c2f 100644
--- a/kernel/fork.c
+++ b/kernel/fork.c
@@ -2006,6 +2006,9 @@ static bool need_futex_hash_allocate_default(u64 clone_flags)
* It copies the registers, and all the appropriate
* parts of the process environment (as per the clone
* flags). The actual kick-off is left to the caller.
+ *
+ * Except for init_struct_pid, a caller-supplied pid reference remains owned
+ * by the caller on failure and is transferred to the new task on success.
*/
__latent_entropy struct task_struct *copy_process(
struct pid *pid,
@@ -2017,6 +2020,7 @@ __latent_entropy struct task_struct *copy_process(
struct task_struct *p;
struct multiprocess_signals delayed;
struct file *pidfile = NULL;
+ bool allocated_pid = false;
const u64 clone_flags = args->flags;
struct nsproxy *nsp = current->nsproxy;
@@ -2317,13 +2321,21 @@ __latent_entropy struct task_struct *copy_process(
stackleak_task_init(p);
- if (pid != &init_struct_pid) {
+ if (!pid) {
pid = alloc_pid(p->nsproxy->pid_ns_for_children, args->set_tid,
args->set_tid_size);
if (IS_ERR(pid)) {
retval = PTR_ERR(pid);
goto bad_fork_cleanup_thread;
}
+ allocated_pid = true;
+ } else if (pid != &init_struct_pid) {
+ if (args->set_tid_size ||
+ ns_of_pid(pid) != p->nsproxy->pid_ns_for_children ||
+ WARN_ON_ONCE(pid_has_task(pid, PIDTYPE_PID))) {
+ retval = -EINVAL;
+ goto bad_fork_cleanup_thread;
+ }
}
/*
@@ -2587,7 +2599,7 @@ __latent_entropy struct task_struct *copy_process(
put_unused_fd(pidfd);
}
bad_fork_free_pid:
- if (pid != &init_struct_pid)
+ if (allocated_pid)
free_pid(pid);
bad_fork_cleanup_thread:
exit_thread(p);
diff --git a/kernel/pid.c b/kernel/pid.c
index f55189a3d07d4..010f80177cac8 100644
--- a/kernel/pid.c
+++ b/kernel/pid.c
@@ -156,8 +156,8 @@ void free_pids(struct pid **pids)
free_pid(pids[tmp]);
}
-struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
- size_t arg_set_tid_size)
+static struct pid *__alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
+ size_t arg_set_tid_size, u64 pidfs_ino)
{
int set_tid[MAX_PID_NS_LEVEL + 1] = {};
int pid_max[MAX_PID_NS_LEVEL + 1] = {};
@@ -198,6 +198,7 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
init_waitqueue_head(&pid->wait_pidfd);
INIT_HLIST_HEAD(&pid->inodes);
pidfs_prepare_pid(pid);
+ pid->ino = pidfs_ino;
/*
* 2. perm check checkpoint_restore_ns_capable()
@@ -358,6 +359,23 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
return ERR_PTR(retval);
}
+struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid,
+ size_t arg_set_tid_size)
+{
+ return __alloc_pid(ns, arg_set_tid, arg_set_tid_size, 0);
+}
+
+struct pid *alloc_pid_with_pidfs_ino(struct pid_namespace *ns,
+ pid_t *arg_set_tid,
+ size_t arg_set_tid_size,
+ u64 pidfs_ino)
+{
+ if (!pidfs_ino)
+ return ERR_PTR(-EINVAL);
+
+ return __alloc_pid(ns, arg_set_tid, arg_set_tid_size, pidfs_ino);
+}
+
void disable_pid_allocation(struct pid_namespace *ns)
{
spin_lock(&pidmap_lock);
--
2.52.0