]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
bpf,fork: wipe ->bpf_storage before bailouts that access it
authorJann Horn <jannh@google.com>
Fri, 26 Jun 2026 15:52:52 +0000 (17:52 +0200)
committerAndrii Nakryiko <andrii@kernel.org>
Mon, 29 Jun 2026 22:08:23 +0000 (15:08 -0700)
Currently, copy_process() can bail out to free_task() before p->bpf_storage
has been initialized, with this call graph (shown here for the
!CONFIG_MEMCG case):

copy_process
  dup_task_struct
    arch_dup_task_struct
      [copies the entire task_struct, including ->bpf_storage member]
  [RLIMIT_NPROC check fails]
  delayed_free_task
    free_task
      bpf_task_storage_free
        rcu_dereference(task->bpf_storage)
        bpf_local_storage_destroy

In this case, the nascent task's ->bpf_storage member that
bpf_local_storage_destroy() operates on is a plain copy of the parent's
->bpf_storage pointer, not a real initialized pointer.
This leads to badness (kernel hangs, UAF).

This is reachable as long as the process calling fork() has been inserted
into a task storage map.

Cc: stable@kernel.org
Fixes: a10787e6d58c ("bpf: Enable task local storage for tracing programs")
Signed-off-by: Jann Horn <jannh@google.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
kernel/fork.c

index 13e38e89a1f3037f38743d53d8813376637b0496..f0e2e131a9a5af7b25e71c1d28af1a6aebdc4319 100644 (file)
@@ -1009,6 +1009,11 @@ static struct task_struct *dup_task_struct(struct task_struct *orig, int node)
        tsk->mm_cid.active = 0;
        INIT_HLIST_NODE(&tsk->mm_cid.node);
 #endif
+
+#ifdef CONFIG_BPF_SYSCALL
+       RCU_INIT_POINTER(tsk->bpf_storage, NULL);
+       tsk->bpf_ctx = NULL;
+#endif
        return tsk;
 
 free_stack:
@@ -2247,10 +2252,6 @@ __latent_entropy struct task_struct *copy_process(
        p->sequential_io        = 0;
        p->sequential_io_avg    = 0;
 #endif
-#ifdef CONFIG_BPF_SYSCALL
-       RCU_INIT_POINTER(p->bpf_storage, NULL);
-       p->bpf_ctx = NULL;
-#endif
 
        unwind_task_init(p);