Re: [PATCH] mm/vma: restore VMA iterator on full-range merge failure

From: Lorenzo Stoakes (ARM)

Date: Sat Nov 14 2026 - 12:15:11 EST


On Sun, Nov 15, 2026 at 12:46:55AM +0800, shao.mingyin@xxxxxxxxxx wrote:
> From: Shao Mingyin <shao.mingyin@xxxxxxxxxx>
>
> mm/vma: restore VMA iterator on full-range merge failure
>
> vma_merge_existing_range() moves the iterator to the previous VMA when
> merging to the left. If the range spans the whole middle VMA and a
> vm_ops->close() hook prevents removing it, the function returns without
> restoring the iterator.
>
> An mprotect() walk can then visit the middle VMA again. Restore the
> iterator to the range start, as the abort path already does. Extend the
> existing close-hook merge test to check the iterator after this failure.
> No crash or data corruption has been observed in this case.
>
> Fixes: 01c373e9a5ce2 ("mm: rework vm_ops->close() handling on VMA merge")
> Signed-off-by: Shao Mingyin <shao.mingyin@xxxxxxxxxx>

(Not the first time I've seen this 'issue' reported).

This is pointless. No caller cares that the merge doesn't
proceed. Revisting it is a noop.

So this isn't a bug and the 'fix' achieves nothing except unnecessary
additional work.

I might go change how this behaves just to stop AI-generated reports... :)

> ---
> Tested with tools/testing/vma using its default ASan/UBSan options.
> The added iterator assertion fails before the fix; all 29 tests pass
> with the fix. Also build-tested mm/vma.o with W=1 on x86_64.
> This verifies iterator position, not a crash or a production workload.

Generally we expect people to mention their LLM usage here.

>
>
> mm/vma.c | 5 ++++-
> tools/testing/vma/tests/merge.c | 1 +
> 2 files changed, 5 insertions(+), 1 deletion(-)
>
> diff --git a/mm/vma.c b/mm/vma.c
> index c82e9306b11ac14f684f44b8d71a0026b7fc145c..c87b29f1beec4ceb02c23f6b37af0e5dff272436 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -983,8 +983,11 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
> * If we need to remove middle in its entirety but are unable to do so,
> * we have no sensible recourse but to abort the merge.
> */
> - if (vmg->__remove_middle && !can_merge_remove_vma(middle))
> + if (vmg->__remove_middle && !can_merge_remove_vma(middle)) {
> + vma_iter_set(vmg->vmi, start);
> + vma_iter_load(vmg->vmi);
> return NULL;
> + }
>
> /* If middle isn't removed then it's resized, i.e. splits. */
> if (!vmg->__remove_middle && check_split(middle, left_side ? end : start))
> diff --git a/tools/testing/vma/tests/merge.c b/tools/testing/vma/tests/merge.c
> index b26f1a66a17074775d751a907eb627445cc1dda3..91accb5dc9944724cd11d1bfa47484a3cf9df54c 100644
> --- a/tools/testing/vma/tests/merge.c
> +++ b/tools/testing/vma/tests/merge.c
> @@ -680,6 +680,7 @@ static bool test_vma_merge_with_close(void)
> */
> ASSERT_EQ(merge_existing(&vmg), NULL);
> ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
> + ASSERT_EQ(vma_iter_addr(&vmi), 0x3000);
>
> ASSERT_EQ(cleanup_mm(&mm, &vmi), 2);
>
>
> base-commit: 84abb955b1015477aa11a96e2f3ac1b938b77f48
> --
> 2.43.0

--
Cheers, Lorenzo