* [PATCH v2] bpf: Fix use-after-free on mm_struct in bpf_find_vma()
@ 2026-05-29 8:01 Sanghyun Park
2026-06-02 5:55 ` Yonghong Song
0 siblings, 1 reply; 2+ messages in thread
From: Sanghyun Park @ 2026-05-29 8:01 UTC (permalink / raw)
To: ast, daniel, andrii; +Cc: bpf, linux-kernel, puranjay
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 <sanghyun.park.cnu@gmail.com>
---
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
^ permalink raw reply related [flat|nested] 2+ messages in thread
* Re: [PATCH v2] bpf: Fix use-after-free on mm_struct in bpf_find_vma()
2026-05-29 8:01 [PATCH v2] bpf: Fix use-after-free on mm_struct in bpf_find_vma() Sanghyun Park
@ 2026-06-02 5:55 ` Yonghong Song
0 siblings, 0 replies; 2+ messages in thread
From: Yonghong Song @ 2026-06-02 5:55 UTC (permalink / raw)
To: Sanghyun Park, ast, daniel, andrii; +Cc: bpf, linux-kernel, puranjay
On 5/29/26 1:01 AM, Sanghyun Park wrote:
> 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 <sanghyun.park.cnu@gmail.com>
> ---
> 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);
mmput may sleep. See the code:
void mmput(struct mm_struct *mm)
{
might_sleep();
if (atomic_dec_and_test(&mm->mm_users))
__mmput(mm);
}
But bpf_find_vma may be in non-sleepable prog context (rcu context).
So the fix does not work.
Also, there is a format issue in the diff.
> return ret;
> }
>
^ permalink raw reply [flat|nested] 2+ messages in thread
end of thread, other threads:[~2026-06-02 5:55 UTC | newest]
Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-29 8:01 [PATCH v2] bpf: Fix use-after-free on mm_struct in bpf_find_vma() Sanghyun Park
2026-06-02 5:55 ` Yonghong Song
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox