Skip to content

bpf: Fix use-after-free on mm_struct in bpf_find_vma()#12421

Open
kernel-patches-daemon-bpf[bot] wants to merge 1 commit into
bpf-next_basefrom
series/1108917=>bpf-next
Open

bpf: Fix use-after-free on mm_struct in bpf_find_vma()#12421
kernel-patches-daemon-bpf[bot] wants to merge 1 commit into
bpf-next_basefrom
series/1108917=>bpf-next

Conversation

@kernel-patches-daemon-bpf

Copy link
Copy Markdown

Pull request for series with
subject: bpf: Fix use-after-free on mm_struct in bpf_find_vma()
version: 4
url: https://patchwork.kernel.org/project/netdevbpf/list/?series=1108917

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: c15261b
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1108917
version: 4

@kernel-patches-review-bot

Copy link
Copy Markdown
commit ed28ea92b474489c6a438d0a444da3bb091b4675
Author: Sanghyun Park <sanghyun.park.cnu@gmail.com>

bpf: Fix use-after-free on mm_struct in bpf_find_vma()

This commit fixes a use-after-free vulnerability in bpf_find_vma() where
a foreign task's mm_struct could be freed by a concurrent exit_mm() between
reading task->mm and calling mmap_read_trylock(). The fix pins the mm under
task->alloc_lock for foreign tasks and releases it with mmput_async().

Link: https://lore.kernel.org/bpf/20260610025906.3E7E71F00893@smtp.kernel.org/

> diff --git a/kernel/bpf/task_iter.c b/kernel/bpf/task_iter.c
> index e791ae065c39..77f90b887bf1 100644
> --- a/kernel/bpf/task_iter.c
> +++ b/kernel/bpf/task_iter.c

[ ... ]

> @@ -765,14 +775,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);
                         ^^^^^^^^

sashiko-bot@kernel.org raised a concern about this in v4: bpf_find_vma()
traverses the VMA tree of an external mm_struct (from a foreign task) without
checking check_stable_address_space(mm).

When inspecting a foreign task, the mm_struct could be flagged with
MMF_UNSTABLE if it was subject to an OOM reap or a dup_mmap() failure. In this
state, the maple tree can contain XA_ZERO_ENTRY markers.

If find_vma() returns one of these markers, could the subsequent check using
vma->vm_start dereference an internal integer value and cause a kernel panic?

>
> @@ -782,6 +816,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);
>  	return ret;
>  }



AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md

In-Reply-To-Subject: bpf: Fix use-after-free on mm_struct in bpf_find_vma()
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/27250036662

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 140fa23
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1108917
version: 4

@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 2e8ad1f
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1108917
version: 4

1 similar comment
@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 2e8ad1f
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1108917
version: 4

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
d8e27d2 ("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: 7c7e3d3 ("bpf: Introduce helper bpf_find_vma")
Signed-off-by: Sanghyun Park <sanghyun.park.cnu@gmail.com>
@kernel-patches-daemon-bpf

Copy link
Copy Markdown
Author

Upstream branch: 30dee2c
series: https://patchwork.kernel.org/project/netdevbpf/list/?series=1108917
version: 4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant