[RFC PATCH 09/24] fork: let process builders supply preallocated pids
From: Li Chen <hidden>
Date: 2026-07-16 14:37:58
Also in:
linux-arch, linux-doc, linux-fsdevel, linux-kselftest, linux-mm, linux-security-module, lkml
Subsystem:
exec & binfmt api, elf, memory management - core, scheduler, the rest · Maintainers:
Kees Cook, Andrew Morton, David Hildenbrand, Ingo Molnar, Peter Zijlstra, Juri Lelli, Vincent Guittot, Linus Torvalds
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 <redacted> --- 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