Linux-mm Archive on lore.kernel.org
 help / color / mirror / Atom feed
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


      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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox