From: Yonghong Song <yonghong.song@linux.dev>
To: Sanghyun Park <sanghyun.park.cnu@gmail.com>,
Alexei Starovoitov <ast@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Andrii Nakryiko <andrii@kernel.org>
Cc: Martin KaFai Lau <martin.lau@linux.dev>,
Eduard Zingerman <eddyz87@gmail.com>, Song Liu <song@kernel.org>,
Kumar Kartikeya Dwivedi <memxor@gmail.com>,
John Fastabend <john.fastabend@gmail.com>,
KP Singh <kpsingh@kernel.org>,
Stanislav Fomichev <sdf@fomichev.me>, Hao Luo <haoluo@google.com>,
Jiri Olsa <jolsa@kernel.org>,
Emil Tsalapatis <emil@etsalapatis.com>,
Puranjay Mohan <puranjay@kernel.org>,
bpf@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: Re: [PATCH bpf-next v4] bpf: Fix use-after-free on mm_struct in bpf_find_vma()
Date: Sat, 20 Jun 2026 09:26:09 -0700 [thread overview]
Message-ID: <01d0ad15-679e-4730-949a-5eae77576cbc@linux.dev> (raw)
In-Reply-To: <20260610024637.343364-1-sanghyun.park.cnu@gmail.com>
On 6/9/26 7:46 PM, Sanghyun Park wrote:
> bpf_find_vma() reads task->mm and calls mmap_read_trylock(mm) without
> holding a reference on the mm. On a foreign task, a concurrent exit_mm()
> can free the mm_struct between the lockless read and the trylock,
> resulting in a use-after-free. mm_struct is not SLAB_TYPESAFE_BY_RCU.
>
> For the current task, task->mm is stable. For a foreign task, pin the mm
> under task->alloc_lock and release it with mmput_async(), mirroring commit
> d8e27d2d22b6 ("bpf: fix mm lifecycle in open-coded task_vma iterator").
> Use spin_trylock() instead of get_task_mm() so BPF context does not block
> on alloc_lock. Reject irqs-disabled contexts and !CONFIG_MMU on the
> foreign-task path because dropping the mm reference is not safe there.
>
> Race:
>
> CPU0 (BPF program) CPU1 (exiting task)
> ============================ ==========================
> bpf_find_vma(foreign_task):
> mm = task->mm
> exit_mm():
> task->mm = NULL
> mmput(mm) -> frees mm_struct
> mmap_read_trylock(mm)
> // UAF on mm
>
> Fixes: 7c7e3d31e785 ("bpf: Introduce helper bpf_find_vma")
> Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com>
LGTM with a nit below.
Acked-by: Yonghong Song <yonghong.song@linux.dev>
> ---
> v4:
> - Use [PATCH bpf-next] subject as requested by Alexei.
> - Add the missing BPF maintainers/reviewers to Cc.
> v3: https://lore.kernel.org/bpf/20260609105216.3536839-1-sanghyun.park.cnu@gmail.com/
> - Drop get_task_mm()+mmput(); mirror d8e27d2d22b6 with alloc_lock
> trylock + mmput_async(). (Yonghong Song)
> - Reject irqs-disabled contexts on the foreign-task path.
> - Reject foreign-task path when !CONFIG_MMU: bpf_iter_mmput_async()
> falls back to mmput() which may sleep, and bpf_find_vma() can run
> in non-sleepable context.
> - Shorten the foreign-task rationale comment and trim the changelog body.
> - Fix the v2's whitespace damage.
> v2: https://lore.kernel.org/bpf/CAOrxSK5_7e4114VyfEU9htGi+UneuNt88fGVKOAa3_ZenPOFkA@mail.gmail.com/
>
> kernel/bpf/task_iter.c | 50 +++++++++++++++++++++++++++++++++++-------
> 1 file changed, 42 insertions(+), 8 deletions(-)
>
> diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
> index fc5f463ca5..baee813290 100644
> --- a/kernel/bpf/task_iter.c
> +++ b/kernel/bpf/task_iter.c
> @@ -754,12 +754,22 @@ static struct bpf_iter_reg task_vma_reg_info = {
> .show_fdinfo = bpf_iter_task_show_fdinfo,
> };
>
> +static inline void bpf_iter_mmput_async(struct mm_struct *mm)
> +{
> +#ifdef CONFIG_MMU
> + mmput_async(mm);
> +#else
> + mmput(mm);
> +#endif
> +}
> +
> BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
> bpf_callback_t, callback_fn, void *, callback_ctx, u64, flags)
> {
> struct mmap_unlock_irq_work *work = NULL;
> struct vm_area_struct *vma;
> bool irq_work_busy = false;
> + bool mmput_needed = false;
> struct mm_struct *mm;
> int ret = -ENOENT;
>
> @@ -769,14 +779,38 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
> if (!task)
> return -ENOENT;
>
> - mm = task->mm;
> + if (task == current) {
> + mm = task->mm;
> + } else {
> + /*
> + * Foreign task: pin task->mm against a concurrent exit_mm().
> + * Use trylock on alloc_lock instead of get_task_mm()'s
> + * blocking task_lock() to avoid deadlocking the target task.
> + */
> + if (!IS_ENABLED(CONFIG_MMU))
> + return -EOPNOTSUPP;
> + if (irqs_disabled())
> + return -EBUSY;
> + if (!spin_trylock(&task->alloc_lock))
> + return -EBUSY;
> + mm = task->mm;
> + if (mm && !(task->flags & PF_KTHREAD)) {
> + mmget(mm);
> + mmput_needed = true;
> + } else {
> + mm = NULL;
> + }
> + spin_unlock(&task->alloc_lock);
> + }
> if (!mm)
> return -ENOENT;
>
> irq_work_busy = bpf_mmap_unlock_get_irq_work(&work);
>
> - if (irq_work_busy || !mmap_read_trylock(mm))
> - return -EBUSY;
> + if (irq_work_busy || !mmap_read_trylock(mm)) {
> + ret = -EBUSY;
> + goto out;
> + }
>
> vma = find_vma(mm, start);
>
> @@ -786,6 +820,9 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
> ret = 0;
> }
> bpf_mmap_unlock_mm(work, mm);
> +out:
> + if (mmput_needed)
> + bpf_iter_mmput_async(mm);
mmput_needed is true requiring CONFIG_MMU enabled, so the above
bpf_iter_mmput_async(mm) can be replaced with mmput_async(mm).
This will make code easier to understand.
> return ret;
> }
>
> @@ -800,15 +837,6 @@ const struct bpf_func_proto bpf_find_vma_proto = {
> .arg5_type = ARG_ANYTHING,
> };
>
> -static inline void bpf_iter_mmput_async(struct mm_struct *mm)
> -{
> -#ifdef CONFIG_MMU
> - mmput_async(mm);
> -#else
> - mmput(mm);
> -#endif
> -}
> -
> struct bpf_iter_task_vma_kern_data {
> struct task_struct *task;
> struct mm_struct *mm;
prev parent reply other threads:[~2026-06-20 16:26 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-10 2:46 [PATCH bpf-next v4] bpf: Fix use-after-free on mm_struct in bpf_find_vma() Sanghyun Park
2026-06-10 2:59 ` sashiko-bot
2026-06-10 3:16 ` bot+bpf-ci
2026-06-10 4:23 ` Sanghyun Park
2026-06-20 16:26 ` 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=01d0ad15-679e-4730-949a-5eae77576cbc@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=eddyz87@gmail.com \
--cc=emil@etsalapatis.com \
--cc=haoluo@google.com \
--cc=john.fastabend@gmail.com \
--cc=jolsa@kernel.org \
--cc=kpsingh@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=martin.lau@linux.dev \
--cc=memxor@gmail.com \
--cc=puranjay@kernel.org \
--cc=sanghyun.park.cnu@gmail.com \
--cc=sdf@fomichev.me \
--cc=song@kernel.org \
/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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.