* [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma()
@ 2026-06-30 3:25 Sanghyun Park
2026-07-01 19:48 ` Andrii Nakryiko
2026-07-07 7:50 ` patchwork-bot+netdevbpf
0 siblings, 2 replies; 8+ messages in thread
From: Sanghyun Park @ 2026-06-30 3:25 UTC (permalink / raw)
To: Yonghong Song, Alexei Starovoitov, Daniel Borkmann,
Andrii Nakryiko
Cc: Sanghyun Park, Martin KaFai Lau, Eduard Zingerman, Song Liu,
Kumar Kartikeya Dwivedi, John Fastabend, KP Singh,
Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis,
Puranjay Mohan, bpf, linux-kernel
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")
Acked-by: Yonghong Song <yonghong.song@linux.dev>
Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com>
---
v6:
- Restore bpf_iter_mmput_async() because bpf_iter_task_vma_destroy()
still uses it.
- Keep the direct mmput_async() call in bpf_find_vma(), but guard it with
CONFIG_MMU because mmput_async() is declared only for CONFIG_MMU builds.
v5: https://lore.kernel.org/bpf/20260630023443.3026627-2-sanghyun.park.cnu@gmail.com/
- Replace bpf_iter_mmput_async() with mmput_async() directly as suggested
by Yonghong.
- Add Yonghong's Acked-by.
v4: https://lore.kernel.org/bpf/20260610024637.343364-1-sanghyun.park.cnu@gmail.com/
- 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 | 36 +++++++++++++++++++++++++++++++++---
1 file changed, 33 insertions(+), 3 deletions(-)
diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
index fc5f463ca529a..1d56193caf5e4 100644
--- a/kernel/bpf/task_iter.c
+++ b/kernel/bpf/task_iter.c
@@ -760,6 +760,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
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 +770,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 +811,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start,
ret = 0;
}
bpf_mmap_unlock_mm(work, mm);
+out:
+#ifdef CONFIG_MMU
+ if (mmput_needed)
+ mmput_async(mm);
+#endif
return ret;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-06-30 3:25 [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() Sanghyun Park @ 2026-07-01 19:48 ` Andrii Nakryiko 2026-07-06 11:28 ` Puranjay Mohan 2026-07-07 7:50 ` patchwork-bot+netdevbpf 1 sibling, 1 reply; 8+ messages in thread From: Andrii Nakryiko @ 2026-07-01 19:48 UTC (permalink / raw) To: Sanghyun Park, Puranjay Mohan Cc: Yonghong Song, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Kumar Kartikeya Dwivedi, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis, bpf, linux-kernel On Mon, Jun 29, 2026 at 8:25 PM Sanghyun Park <sanghyun.park.cnu@gmail.com> 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") > Acked-by: Yonghong Song <yonghong.song@linux.dev> > Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com> > --- Puranjay, PTAL, I think you've been fixing similar issue in bpf_iter_task_vma_new(), would be nice for you to check this fix as well. Thanks! > v6: > - Restore bpf_iter_mmput_async() because bpf_iter_task_vma_destroy() > still uses it. > - Keep the direct mmput_async() call in bpf_find_vma(), but guard it with > CONFIG_MMU because mmput_async() is declared only for CONFIG_MMU builds. > v5: https://lore.kernel.org/bpf/20260630023443.3026627-2-sanghyun.park.cnu@gmail.com/ > - Replace bpf_iter_mmput_async() with mmput_async() directly as suggested > by Yonghong. > - Add Yonghong's Acked-by. > v4: https://lore.kernel.org/bpf/20260610024637.343364-1-sanghyun.park.cnu@gmail.com/ > - 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 | 36 +++++++++++++++++++++++++++++++++--- > 1 file changed, 33 insertions(+), 3 deletions(-) > > diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c > index fc5f463ca529a..1d56193caf5e4 100644 > --- a/kernel/bpf/task_iter.c > +++ b/kernel/bpf/task_iter.c > @@ -760,6 +760,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, > 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 +770,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 +811,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, > ret = 0; > } > bpf_mmap_unlock_mm(work, mm); > +out: > +#ifdef CONFIG_MMU > + if (mmput_needed) > + mmput_async(mm); > +#endif > return ret; > } > > -- > 2.48.1 ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-07-01 19:48 ` Andrii Nakryiko @ 2026-07-06 11:28 ` Puranjay Mohan 2026-07-07 7:43 ` Daniel Borkmann 0 siblings, 1 reply; 8+ messages in thread From: Puranjay Mohan @ 2026-07-06 11:28 UTC (permalink / raw) To: Andrii Nakryiko Cc: Sanghyun Park, Yonghong Song, Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Kumar Kartikeya Dwivedi, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis, bpf, linux-kernel On Wed, Jul 1, 2026 at 8:48 PM Andrii Nakryiko <andrii.nakryiko@gmail.com> wrote: > > On Mon, Jun 29, 2026 at 8:25 PM Sanghyun Park > <sanghyun.park.cnu@gmail.com> 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") > > Acked-by: Yonghong Song <yonghong.song@linux.dev> > > Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com> > > --- > > Puranjay, PTAL, I think you've been fixing similar issue in > bpf_iter_task_vma_new(), would be nice for you to check this fix as > well. Thanks! > > > v6: > > - Restore bpf_iter_mmput_async() because bpf_iter_task_vma_destroy() > > still uses it. > > - Keep the direct mmput_async() call in bpf_find_vma(), but guard it with > > CONFIG_MMU because mmput_async() is declared only for CONFIG_MMU builds. > > v5: https://lore.kernel.org/bpf/20260630023443.3026627-2-sanghyun.park.cnu@gmail.com/ > > - Replace bpf_iter_mmput_async() with mmput_async() directly as suggested > > by Yonghong. > > - Add Yonghong's Acked-by. > > v4: https://lore.kernel.org/bpf/20260610024637.343364-1-sanghyun.park.cnu@gmail.com/ > > - 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 | 36 +++++++++++++++++++++++++++++++++--- > > 1 file changed, 33 insertions(+), 3 deletions(-) > > > > diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c > > index fc5f463ca529a..1d56193caf5e4 100644 > > --- a/kernel/bpf/task_iter.c > > +++ b/kernel/bpf/task_iter.c > > @@ -760,6 +760,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, > > 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 +770,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 +811,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, > > ret = 0; > > } > > bpf_mmap_unlock_mm(work, mm); > > +out: > > +#ifdef CONFIG_MMU > > + if (mmput_needed) > > + mmput_async(mm); > > +#endif > > return ret; > > } > > > > -- > > 2.48.1 Thanks for this, it looks good to me. Reviewed-by: Puranjay Mohan <puranjay@kernel.org> ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-07-06 11:28 ` Puranjay Mohan @ 2026-07-07 7:43 ` Daniel Borkmann 2026-07-08 5:28 ` Sanghyun Park 0 siblings, 1 reply; 8+ messages in thread From: Daniel Borkmann @ 2026-07-07 7:43 UTC (permalink / raw) To: Puranjay Mohan, Andrii Nakryiko Cc: Sanghyun Park, Yonghong Song, Alexei Starovoitov, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Kumar Kartikeya Dwivedi, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis, bpf, linux-kernel On 7/6/26 1:28 PM, Puranjay Mohan wrote: > On Wed, Jul 1, 2026 at 8:48 PM Andrii Nakryiko > <andrii.nakryiko@gmail.com> wrote: >> >> On Mon, Jun 29, 2026 at 8:25 PM Sanghyun Park >> <sanghyun.park.cnu@gmail.com> 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") >>> Acked-by: Yonghong Song <yonghong.song@linux.dev> >>> Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com> >>> --- >> >> Puranjay, PTAL, I think you've been fixing similar issue in >> bpf_iter_task_vma_new(), would be nice for you to check this fix as >> well. Thanks! >> >>> v6: >>> - Restore bpf_iter_mmput_async() because bpf_iter_task_vma_destroy() >>> still uses it. >>> - Keep the direct mmput_async() call in bpf_find_vma(), but guard it with >>> CONFIG_MMU because mmput_async() is declared only for CONFIG_MMU builds. >>> v5: https://lore.kernel.org/bpf/20260630023443.3026627-2-sanghyun.park.cnu@gmail.com/ >>> - Replace bpf_iter_mmput_async() with mmput_async() directly as suggested >>> by Yonghong. >>> - Add Yonghong's Acked-by. >>> v4: https://lore.kernel.org/bpf/20260610024637.343364-1-sanghyun.park.cnu@gmail.com/ >>> - 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 | 36 +++++++++++++++++++++++++++++++++--- >>> 1 file changed, 33 insertions(+), 3 deletions(-) >>> >>> diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c >>> index fc5f463ca529a..1d56193caf5e4 100644 >>> --- a/kernel/bpf/task_iter.c >>> +++ b/kernel/bpf/task_iter.c >>> @@ -760,6 +760,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, >>> 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 +770,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 +811,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, >>> ret = 0; >>> } >>> bpf_mmap_unlock_mm(work, mm); >>> +out: >>> +#ifdef CONFIG_MMU >>> + if (mmput_needed) >>> + mmput_async(mm); >>> +#endif >>> return ret; >>> } >>> >>> -- >>> 2.48.1 > > Thanks for this, it looks good to me. > > Reviewed-by: Puranjay Mohan <puranjay@kernel.org> Applied, thanks! Could either you or Sanghyun look into the pre-existing sashiko-reported issue as well? ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-07-07 7:43 ` Daniel Borkmann @ 2026-07-08 5:28 ` Sanghyun Park 2026-07-08 5:38 ` Kumar Kartikeya Dwivedi 0 siblings, 1 reply; 8+ messages in thread From: Sanghyun Park @ 2026-07-08 5:28 UTC (permalink / raw) To: Daniel Borkmann Cc: Puranjay Mohan, Andrii Nakryiko, Yonghong Song, Alexei Starovoitov, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Kumar Kartikeya Dwivedi, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis, bpf, linux-kernel Sure, I’ll take a look at that issue once the current patch is sorted out. 2026년 7월 7일 (화) 오후 4:44, Daniel Borkmann <daniel@iogearbox.net>님이 작성: > > On 7/6/26 1:28 PM, Puranjay Mohan wrote: > > On Wed, Jul 1, 2026 at 8:48 PM Andrii Nakryiko > > <andrii.nakryiko@gmail.com> wrote: > >> > >> On Mon, Jun 29, 2026 at 8:25 PM Sanghyun Park > >> <sanghyun.park.cnu@gmail.com> 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") > >>> Acked-by: Yonghong Song <yonghong.song@linux.dev> > >>> Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com> > >>> --- > >> > >> Puranjay, PTAL, I think you've been fixing similar issue in > >> bpf_iter_task_vma_new(), would be nice for you to check this fix as > >> well. Thanks! > >> > >>> v6: > >>> - Restore bpf_iter_mmput_async() because bpf_iter_task_vma_destroy() > >>> still uses it. > >>> - Keep the direct mmput_async() call in bpf_find_vma(), but guard it with > >>> CONFIG_MMU because mmput_async() is declared only for CONFIG_MMU builds. > >>> v5: https://lore.kernel.org/bpf/20260630023443.3026627-2-sanghyun.park.cnu@gmail.com/ > >>> - Replace bpf_iter_mmput_async() with mmput_async() directly as suggested > >>> by Yonghong. > >>> - Add Yonghong's Acked-by. > >>> v4: https://lore.kernel.org/bpf/20260610024637.343364-1-sanghyun.park.cnu@gmail.com/ > >>> - 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 | 36 +++++++++++++++++++++++++++++++++--- > >>> 1 file changed, 33 insertions(+), 3 deletions(-) > >>> > >>> diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c > >>> index fc5f463ca529a..1d56193caf5e4 100644 > >>> --- a/kernel/bpf/task_iter.c > >>> +++ b/kernel/bpf/task_iter.c > >>> @@ -760,6 +760,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, > >>> 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 +770,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 +811,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, > >>> ret = 0; > >>> } > >>> bpf_mmap_unlock_mm(work, mm); > >>> +out: > >>> +#ifdef CONFIG_MMU > >>> + if (mmput_needed) > >>> + mmput_async(mm); > >>> +#endif > >>> return ret; > >>> } > >>> > >>> -- > >>> 2.48.1 > > > > Thanks for this, it looks good to me. > > > > Reviewed-by: Puranjay Mohan <puranjay@kernel.org> > > Applied, thanks! Could either you or Sanghyun look into the pre-existing > sashiko-reported issue as well? ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-07-08 5:28 ` Sanghyun Park @ 2026-07-08 5:38 ` Kumar Kartikeya Dwivedi 2026-07-08 7:13 ` Sanghyun Park 0 siblings, 1 reply; 8+ messages in thread From: Kumar Kartikeya Dwivedi @ 2026-07-08 5:38 UTC (permalink / raw) To: Sanghyun Park, Daniel Borkmann Cc: Puranjay Mohan, Andrii Nakryiko, Yonghong Song, Alexei Starovoitov, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, Kumar Kartikeya Dwivedi, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis, bpf, linux-kernel On Wed Jul 8, 2026 at 7:28 AM CEST, Sanghyun Park wrote: > Sure, > Please avoid top-posting. > I’ll take a look at that issue once the current patch is sorted out. > We had to drop this from the tree because of build-time warnings [0], since mmput_needed will not be used in case CONFIG_MMU is unset. It should be easy to address by adding __maybe_unused to the mmput_needed's declaration. Could you make that fix and respin? [0]: https://lore.kernel.org/bpf/202607080951.rMUtVX8X-lkp@intel.com > 2026년 7월 7일 (화) 오후 4:44, Daniel Borkmann <daniel@iogearbox.net>님이 작성: >> >> On 7/6/26 1:28 PM, Puranjay Mohan wrote: >> > On Wed, Jul 1, 2026 at 8:48 PM Andrii Nakryiko >> > <andrii.nakryiko@gmail.com> wrote: >> >> >> >> On Mon, Jun 29, 2026 at 8:25 PM Sanghyun Park >> >> <sanghyun.park.cnu@gmail.com> 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") >> >>> Acked-by: Yonghong Song <yonghong.song@linux.dev> >> >>> Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com> >> >>> --- >> >> >> >> Puranjay, PTAL, I think you've been fixing similar issue in >> >> bpf_iter_task_vma_new(), would be nice for you to check this fix as >> >> well. Thanks! >> >> >> >>> v6: >> >>> - Restore bpf_iter_mmput_async() because bpf_iter_task_vma_destroy() >> >>> still uses it. >> >>> - Keep the direct mmput_async() call in bpf_find_vma(), but guard it with >> >>> CONFIG_MMU because mmput_async() is declared only for CONFIG_MMU builds. >> >>> v5: https://lore.kernel.org/bpf/20260630023443.3026627-2-sanghyun.park.cnu@gmail.com/ >> >>> - Replace bpf_iter_mmput_async() with mmput_async() directly as suggested >> >>> by Yonghong. >> >>> - Add Yonghong's Acked-by. >> >>> v4: https://lore.kernel.org/bpf/20260610024637.343364-1-sanghyun.park.cnu@gmail.com/ >> >>> - 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 | 36 +++++++++++++++++++++++++++++++++--- >> >>> 1 file changed, 33 insertions(+), 3 deletions(-) >> >>> >> >>> diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c >> >>> index fc5f463ca529a..1d56193caf5e4 100644 >> >>> --- a/kernel/bpf/task_iter.c >> >>> +++ b/kernel/bpf/task_iter.c >> >>> @@ -760,6 +760,7 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, >> >>> 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 +770,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 +811,11 @@ BPF_CALL_5(bpf_find_vma, struct task_struct *, task, u64, start, >> >>> ret = 0; >> >>> } >> >>> bpf_mmap_unlock_mm(work, mm); >> >>> +out: >> >>> +#ifdef CONFIG_MMU >> >>> + if (mmput_needed) >> >>> + mmput_async(mm); >> >>> +#endif >> >>> return ret; >> >>> } >> >>> >> >>> -- >> >>> 2.48.1 >> > >> > Thanks for this, it looks good to me. >> > >> > Reviewed-by: Puranjay Mohan <puranjay@kernel.org> >> >> Applied, thanks! Could either you or Sanghyun look into the pre-existing >> sashiko-reported issue as well? ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-07-08 5:38 ` Kumar Kartikeya Dwivedi @ 2026-07-08 7:13 ` Sanghyun Park 0 siblings, 0 replies; 8+ messages in thread From: Sanghyun Park @ 2026-07-08 7:13 UTC (permalink / raw) To: Kumar Kartikeya Dwivedi, Daniel Borkmann Cc: Sanghyun Park, Puranjay Mohan, Andrii Nakryiko, Yonghong Song, Alexei Starovoitov, Andrii Nakryiko, Martin KaFai Lau, Eduard Zingerman, Song Liu, John Fastabend, KP Singh, Stanislav Fomichev, Hao Luo, Jiri Olsa, Emil Tsalapatis, bpf, linux-kernel On Wed, Jul 08, 2026 at 07:38:44AM +0200, Kumar Kartikeya Dwivedi wrote: > Please avoid top-posting. Sorry about that. > We had to drop this from the tree because of build-time warnings [0], since > mmput_needed will not be used in case CONFIG_MMU is unset. It should be easy > to address by adding __maybe_unused to the mmput_needed's declaration. Could > you make that fix and respin? > > [0]: https://lore.kernel.org/bpf/202607080951.rMUtVX8X-lkp@intel.com I will send v7 shortly with mmput_needed marked __maybe_unused. ^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() 2026-06-30 3:25 [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() Sanghyun Park 2026-07-01 19:48 ` Andrii Nakryiko @ 2026-07-07 7:50 ` patchwork-bot+netdevbpf 1 sibling, 0 replies; 8+ messages in thread From: patchwork-bot+netdevbpf @ 2026-07-07 7:50 UTC (permalink / raw) To: Sanghyun Park Cc: yonghong.song, ast, daniel, andrii, martin.lau, eddyz87, song, memxor, john.fastabend, kpsingh, sdf, haoluo, jolsa, emil, puranjay, bpf, linux-kernel Hello: This patch was applied to bpf/bpf-next.git (master) by Daniel Borkmann <daniel@iogearbox.net>: On Tue, 30 Jun 2026 12:25:43 +0900 you 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. > > [...] Here is the summary with links: - [bpf-next,v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() https://git.kernel.org/bpf/bpf-next/c/311e11cfbfbf You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html ^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-07-08 7:13 UTC | newest] Thread overview: 8+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-30 3:25 [PATCH bpf-next v6] bpf: Fix use-after-free on mm_struct in bpf_find_vma() Sanghyun Park 2026-07-01 19:48 ` Andrii Nakryiko 2026-07-06 11:28 ` Puranjay Mohan 2026-07-07 7:43 ` Daniel Borkmann 2026-07-08 5:28 ` Sanghyun Park 2026-07-08 5:38 ` Kumar Kartikeya Dwivedi 2026-07-08 7:13 ` Sanghyun Park 2026-07-07 7:50 ` patchwork-bot+netdevbpf
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox