From 5cf0881344cfa0a3b558107741884d8f43269d7b Mon Sep 17 00:00:00 2001 From: Josef Bacik Date: Tue, 13 Oct 2026 19:25:26 +0000 Subject: [PATCH] fork: Let nobody keep a task that may not come to life dup_task_struct() sets rcu_users to 2. From then on bpf_task_acquire() hands out references to the new task, which BPF programs see as a trusted pointer long before it is sure to live: in the task_alloc LSM hook and in ops.init_task() of sched_ext. If copy_process() fails later on, it undoes what it has done and frees the task without a look at the count. A program that has stored its reference in a map is left with a pointer to freed memory: BUG: KASAN: slab-use-after-free in put_task_struct_rcu_user+0x22/0x90 The program is loaded by the administrator, but any user can make a fork fail, for example at the pids.max of a cgroup. Honouring the count would not do: the task would stay behind with its mm, files and credentials freed. Leave rcu_users at zero until copy_process() cannot fail any more. bpf_task_acquire() returns NULL until then, which its callers have to be prepared for, and which they get for init_task, whose count has always been zero. The other users of rcu_users deal with tasks that have run. Fixes: 90660309b0c7 ("bpf: Add kfuncs for storing struct task_struct * as a kptr") Signed-off-by: Josef Bacik Signed-off-by: Christian Brauner (Amutable) --- kernel/fork.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/kernel/fork.c b/kernel/fork.c index 394906afafd3..3d24f2dbfae9 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -970,10 +970,10 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node) dup_user_cpus_ptr(tsk, orig, node); /* - * One for the user space visible state that goes away when reaped. - * One for the scheduler. + * Nobody can keep the task while copy_process() may still fail, which + * frees it no matter what. */ - refcount_set(&tsk->rcu_users, 2); + refcount_set(&tsk->rcu_users, 0); /* One for the rcu users */ refcount_set(&tsk->usage, 1); #ifdef CONFIG_BLK_DEV_IO_TRACE @@ -2510,6 +2510,12 @@ __latent_entropy struct task_struct *copy_process( /* No more failure paths after this point. */ + /* + * One for the user space visible state that goes away when reaped. + * One for the scheduler. + */ + refcount_set(&p->rcu_users, 2); + /* * Copy seccomp details explicitly here, in case they were changed * before holding sighand lock.