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: 90660309b0 ("bpf: Add kfuncs for storing struct task_struct * as a kptr")
Signed-off-by: Josef Bacik <josef@toxicpanda.com>
Signed-off-by: Christian Brauner (Amutable) <brauner@kernel.org>
This commit is contained in:
Josef Bacik 2026-10-13 19:25:26 +00:00 committed by Christian Brauner
parent aa6439028d
commit 5cf0881344
No known key found for this signature in database
GPG Key ID: 91C61BC06578DCA2

View File

@ -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.