From: sashiko-bot@kernel.org
To: "Luiz Capitulino" <luizcap@redhat.com>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Liam R. Howlett" <liam@infradead.org>,
"Lorenzo Stoakes" <ljs@kernel.org>
Cc: "Vlastimil Babka" <vbabka@kernel.org>,
"Jann Horn" <jannh@google.com>,
"Pedro Falcato" <pfalcato@suse.de>,
"David Hildenbrand" <david@kernel.org>,
"Jakub Matena" <matenajakub@gmail.com>,
linux-mm@kvack.org
Subject: Re: [REGRESSION] mremap() fails VMA expansion when VM_MIXEDMAP is set
Date: Sat, 24 Oct 2026 02:31:33 +0000 [thread overview]
Message-ID: <sashiko-outbox-183514@kernel.org> (raw)
In-Reply-To: <20261023023106.515365-1-luizcap@redhat.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] mm: vma_merge_new_range() merges VM_MIXEDMAP VMAs in mmap() and mremap()
- [Medium] mm: vma: can_merge_vma() breaks test_vma_merge_special_flags() for VM_MIXEDMAP
--
commit b9b12caabde38874f12fe6aa692b1dcf956d3f4a
Author: Luiz Capitulino <luizcap@redhat.com>
mremap() fails VMA expansion when VM_MIXEDMAP is set
This patch allows vma_merge_new_range() to merge VM_MIXEDMAP VMAs while
updating can_merge_remove_vma() to reject removing VM_MIXEDMAP VMAs so
that in-place VMA expansion during mremap() can succeed.
> diff --git a/mm/vma.c b/mm/vma.c
> index f56317ee82471..12ba8e247b477 100644
> --- a/mm/vma.c
> +++ b/mm/vma.c
> @@ -851,9 +851,15 @@ static int commit_merge(struct vma_merge_struct *vmg)
> return 0;
> }
>
> -/* We can only remove VMAs when merging if they do not have a close hook. */
> static bool can_merge_remove_vma(struct vm_area_struct *vma)
> {
> + if (vma_flags_test(&vma->flags, VMA_MIXEDMAP_BIT))
> + return false;
> +
> + /*
> + * We can only remove VMAs when merging if they do not have a
> + * close hook.
> + */
> return !vma->vm_ops || !vma->vm_ops->close;
> }
>
> @@ -1093,6 +1099,20 @@ static __must_check struct vm_area_struct *vma_merge_existing_range(
> return NULL;
> }
>
> +static bool can_merge_vma(const struct vma_merge_struct *vmg)
> +{
> + /* VMAs without prev/next are unmergeable */
> + if (!vmg->prev && !vmg->next)
> + return false;
> +
> + /* We can't merge VMA_IO, VMA_DONTEXPAND or VMA_PFNMAP */
> + if (vma_flags_test_any(&vmg->vma_flags, VMA_IO_BIT, VMA_DONTEXPAND_BIT,
> + VMA_PFNMAP_BIT))
> + return false;
> +
> + return true;
> +}
[Severity: High]
Does omitting VMA_MIXEDMAP_BIT from can_merge_vma() cause unintended merges
of distinct VM_MIXEDMAP VMAs during mmap() and mremap() moves?
In vma_merge_new_range(), can_merge_remove_vma(next) is only checked when
both can_merge_left and can_merge_right are true, and even then it only
reduces a 3-way merge to a 2-way merge into prev:
mm/vma.c:vma_merge_new_range() {
...
if (can_merge_right) {
vmg->end = next->vm_end;
vmg->target = next;
}
if (can_merge_left) {
vmg->start = prev->vm_start;
vmg->target = prev;
...
if (can_merge_right && !can_merge_remove_vma(next))
vmg->end = end;
...
}
Because vma_merge_new_range() is called by __mmap_region() and copy_vma() in
addition to vma_merge_extend(), allowing VMA_MIXEDMAP_BIT here affects two
other paths:
1. In __mmap_region(), when a driver's f_op->mmap_prepare callback sets
VMA_MIXEDMAP_BIT (for example via mmap_action_map_kernel_pages() ->
call_mmap_prepare() -> mmap_action_prepare() -> map_kernel_pages_prepare())
adjacent to a compatible existing VMA, vma_merge_new_range() will now expand
prev or next instead of returning NULL:
mm/vma.c:__mmap_region() {
...
if (map.prev || map.next) {
VMG_MMAP_STATE(vmg, &map, /* vma = */ NULL);
vma = vma_merge_new_range(&vmg);
}
if (!vma) {
error = __mmap_new_vma(&map, &vma, &desc.action);
if (error)
goto unacct_error;
allocated_new = true;
}
if (have_mmap_prepare && !map_is_anon(&map) && allocated_new)
set_vma_user_defined_fields(vma, &map);
__mmap_complete(&map, vma);
if (have_mmap_prepare && allocated_new) {
error = mmap_action_complete(vma, &desc.action,
/*is_compat=*/false);
...
}
When vma_merge_new_range() returns a merged VMA, allocated_new remains
false, skipping both set_vma_user_defined_fields() and
mmap_action_complete() (which skips map_kernel_pages_complete() and
call_vma_mapped()).
2. When moving a VM_MIXEDMAP VMA via mremap() with MREMAP_MAYMOVE or
MREMAP_FIXED adjacent to another compatible VM_MIXEDMAP VMA, move_vma() ->
copy_vma_and_data() -> copy_vma() calls vma_merge_copied_range() ->
vma_merge_new_range(). The moved range is merged into the adjacent VMA
without checking can_merge_remove_vma(vmg->copied_from) and without calling
vm_ops->open() in copy_vma(), after which move_vma() calls
unmap_source_vma() -> do_vmi_munmap() and invokes vm_ops->close() on the
source VMA.
Also, since vma_merge_existing_range() still rejects all VMA_SPECIAL_FLAGS
upfront, is the VMA_MIXEDMAP_BIT check in can_merge_remove_vma() ever
reachable from vma_merge_existing_range()?
> +
> /*
> * vma_merge_new_range - Attempt to merge a new VMA into address space
> *
> @@ -1152,9 +1172,7 @@ struct vm_area_struct *vma_merge_new_range(struct vma_merge_struct *vmg)
>
> vmg->state = VMA_MERGE_NOMERGE;
>
> - /* Special VMAs are unmergeable, also if no prev/next. */
> - if (vma_flags_test_any_mask(&vmg->vma_flags, VMA_SPECIAL_FLAGS) ||
> - (!prev && !next))
> + if (!can_merge_vma(vmg))
> return NULL;
[Severity: Medium]
Does this change cause test_vma_merge_special_flags() in
tools/testing/vma/tests/merge.c to fail on the VMA_MIXEDMAP_BIT iteration?
In test_vma_merge_special_flags(), the loop tests each flag in
special_flags[] (including VMA_MIXEDMAP_BIT) against merge_new() ->
vma_merge_new_range() when adjacent to vma_left:
tools/testing/vma/tests/merge.c:test_vma_merge_special_flags() {
...
vmg_set_range(&vmg, 0x3000, 0x4000, 3, vma_flags);
for (i = 0; i < ARRAY_SIZE(special_flags); i++) {
vma_flag_t special_flag = special_flags[i];
vma_flags_t flags = vma_flags;
vma_flags_set(&flags, special_flag);
vma_left->flags = flags;
vmg.vma_flags = flags;
vma = merge_new(&vmg);
ASSERT_EQ(vma, NULL);
ASSERT_EQ(vmg.state, VMA_MERGE_NOMERGE);
}
...
}
Because can_merge_vma() allows VMA_MIXEDMAP_BIT and vmg.prev is vma_left
with matching flags and pgoff, can_vma_merge_left() and vma_expand()
succeed, returning vma_left with VMA_MERGE_SUCCESS and tripping the
assertions.
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261023023106.515365-1-luizcap@redhat.com?part=1
prev parent reply other threads:[~2026-10-24 2:31 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-23 2:31 [REGRESSION] mremap() fails VMA expansion when VM_MIXEDMAP is set Luiz Capitulino
2026-10-23 7:16 ` Vlastimil Babka (SUSE)
2026-10-23 8:19 ` Lorenzo Stoakes (ARM)
2026-10-23 12:16 ` Luiz Capitulino
2026-10-23 12:13 ` Luiz Capitulino
2026-10-23 13:00 ` Pedro Falcato
2026-10-23 18:16 ` Luiz Capitulino
2026-10-23 8:18 ` Lorenzo Stoakes (ARM)
2026-10-23 12:15 ` Luiz Capitulino
2026-10-23 14:28 ` Lorenzo Stoakes (ARM)
2026-10-24 2:31 ` sashiko-bot [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=sashiko-outbox-183514@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=david@kernel.org \
--cc=jannh@google.com \
--cc=liam@infradead.org \
--cc=linux-mm@kvack.org \
--cc=ljs@kernel.org \
--cc=luizcap@redhat.com \
--cc=matenajakub@gmail.com \
--cc=pfalcato@suse.de \
--cc=sashiko-reviews@lists.linux.dev \
--cc=vbabka@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.