BPF List
 help / color / mirror / Atom feed
From: Yonghong Song <yonghong.song@linux.dev>
To: Sanghyun Park <sanghyun.park.cnu@gmail.com>,
	ast@kernel.org, daniel@iogearbox.net, andrii@kernel.org
Cc: bpf@vger.kernel.org, linux-kernel@vger.kernel.org, puranjay@kernel.org
Subject: Re: [PATCH v2] bpf: Fix use-after-free on mm_struct in bpf_find_vma()
Date: Mon, 1 Jun 2026 22:55:27 -0700	[thread overview]
Message-ID: <a775c0b8-07f3-4265-8cfd-ec01ffd9efbf@linux.dev> (raw)
In-Reply-To: <CAOrxSK5_7e4114VyfEU9htGi+UneuNt88fGVKOAa3_ZenPOFkA@mail.gmail.com>



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;
>   }
>


      reply	other threads:[~2026-06-02  5:55 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
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 message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=a775c0b8-07f3-4265-8cfd-ec01ffd9efbf@linux.dev \
    --to=yonghong.song@linux.dev \
    --cc=andrii@kernel.org \
    --cc=ast@kernel.org \
    --cc=bpf@vger.kernel.org \
    --cc=daniel@iogearbox.net \
    --cc=linux-kernel@vger.kernel.org \
    --cc=puranjay@kernel.org \
    --cc=sanghyun.park.cnu@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox