bpf_find_vma() reads task->mm without holding task_lock() or taking an mm reference via mmget()/mmget_not_zero(). When called on a foreign task obtained via bpf_task_from_pid(), a concurrent exit_mm() can free the mm_struct between the raw pointer read and mmap_read_trylock(mm), resulting in a use-after-free on the mm's mmap_lock. This is the same bug class fixed by commit d8e27d2d22b6 ("bpf: fix mm lifecycle in open-coded task_vma iterator") for the open-coded task_vma iterator, but bpf_find_vma() in the same file was missed by that fix. For the current task, task->mm is stable and needs no extra reference. For a foreign task, use get_task_mm() which acquires task_lock(), checks task->mm, and calls mmget() atomically, preventing the race with exit_mm(). The reference is dropped via mmput() after the mmap lock is released. Race: CPU0 (BPF program) CPU1 (exiting task) ============================ ========================== bpf_find_vma(foreign_task): mm = task->mm // raw read, no reference exit_mm(): task->mm = NULL mmput(mm) -> frees mm_struct mmap_read_trylock(mm) // UAF: mm is freed Reproduction: 1. Build kernel >= 5.17 with CONFIG_KASAN=y, CONFIG_BPF_SYSCALL=y 2. Boot in a VM (QEMU works fine) 3. Compile the reproducer below: gcc -O2 -o repro -static repro.c -lbpf -lelf -lz 4. Run as root: ./repro 5. Check dmesg for: BUG: KASAN: slab-use-after-free in down_read_trylock The reproducer attaches a BPF program that calls bpf_find_vma() on a foreign task obtained via bpf_task_from_pid(). A racing thread repeatedly fork+exit's that task, creating a window where mm is freed. KASAN report (reproduced on 6.12.91, CONFIG_PREEMPT + KASAN): BUG: KASAN: slab-use-after-free in down_read_trylock+0x380/0x3f0 Read of size 8 at addr ffff888003cd2fd0 by task repro/164451 Call Trace: down_read_trylock+0x380/0x3f0 bpf_find_vma+0xdd/0x360 bpf_prog_708df9c9a3e172a7_main_f+0x8b/0x9e bpf_trampoline_6442513469+0x43/0xa3 Freed by task 164453: kmem_cache_free+0x15d/0x4b0 finish_task_switch.isra.0+0x4ab/0x810 Fixes: 7c7e3d31e785 ("bpf: Introduce helper bpf_find_vma") Signed-off-by: Sanghyun Park --- v2: Rebased onto bpf-next to fix merge conflict (no code changes). kernel/bpf/task_iter.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c index e791ae065c..015399e62b 100644 --- a/kernel/bpf/task_iter.c +++ b/kernel/bpf/task_iter.c @@ -757,6 +757,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, struct vm_area_struct *vma; bool irq_work_busy = false; struct mm_struct *mm; + bool foreign = task != current; int ret = -ENOENT; if (flags) @@ -765,7 +766,12 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, if (!task) return -ENOENT; - mm = task->mm; + if (foreign) { + mm = get_task_mm(task); + } else { + mm = task->mm; + } + if (!mm) return -ENOENT; @@ -782,6 +788,8 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, ret = 0; } bpf_mmap_unlock_mm(work, mm); + if (foreign) + mmput(mm); return ret; } -- 2.48.1