Re: [PATCH] mm: prepare anon_vma before swapin rmap

Lorenzo Stoakes
2026-04-19T14:21:50Z

On Sun, Apr 19, 2026 at 10:19:59AM +0200, David Hildenbrand (Arm) wrote:
> On 4/18/26 11:35, Lorenzo Stoakes wrote:
> > On Fri, Apr 17, 2026 at 01:57:59PM +0200, David Hildenbrand (Arm) wrote:
> > > Maybe there was a scenario where we could have lost vma->anon_vma during
> > > a merge, resulting in a swapped page in an anon_vma.
> >
> > Unless there's a bug (and correct me if I'm misinterpreting), VMA merge requires
> > vma->anon_vma to either be equal for merged adjacent VMAs, or one or the other
> > VMA to have NULL vma->anon_vma, in which case we set vma->anon_vma in the merged
> > VMA.
>
> I think you didn't understand what I was trying to say.

Let me take more of a look then!

>
> The reporter claimed that it happened on 6.18. Nobody knows on which patch
> version (stable tree?).
>
> I was wondering whether your fix
>
> commit 3b617fd3d317bf9dd7e2c233e56eafef05734c9d
> Author: Lorenzo Stoakes <ljs@kernel.org>
> Date: Mon Jan 5 20:11:49 2026 +0000
>
> mm/vma: enforce VMA fork limit on unfaulted,faulted mremap merge too
>
> that went into 6.19 might have resolved this problem.

Ahhh, no not that one (it affects merge of VMAs that have a CoW hierarchy which
we shouldn't allow) but 61f67c230a5e actually could cause this.

Can see from https://kernel.dance/#61f67c230a5e it was backported to 6.18.7 I
think.

ZhengYuan - can you try seeing if it repro's with/without that?

If you're testing literally at v6.18 in Linus's tree say and NOT on a stable
tree, then that's your problem - you're essentially testing a known-buggy kernel
(we always find stuff later and send to stable, just how it is).

Fundmentally the bug was that copy_vma() called vma_merge_new_range(), setting
vmg->middle = NULL (becuase a new merge obviously can't be modifying an existing
VMA).

But, uniquely among other merge cases, it merges based on a VMA not adjacent,
and unlike an ordinary new merge, the duplicated VMA might have an anon_vma
associated with it.

And the merge logic had no knowledge of a vma with a vma->anon_vma to even
attempt to copy...

Therefore you _can_ end up with !vma->anon_vma there incorrectly after a merge.

I misremembered that because my fix was very focused on the direct consequence
of this which was a UAF for folio->mapping (!!), but yeah !vma->anon_vma is also
a consequence of that.

So yeah TL;DR it's very likely 61f67c230a5e that fixes this issue (was present
from 6.16 - 6.18, backport to 6.18 because was only relevant stable kernel).

>
> Your fix stated "allow an unfaulted/faulted merge with a VMA that has been
> forked", so I was wondering whether that could have resulted in a situation
> with anon folios without vma->anon_vma (losing vma->anon_vma during the
> merge).

Yeah sorry all this logic is very fiddly. That's about cases where the
vma->anon_vma_chain size is > 1 i.e. where you are a child of a fork operation,
i.e. one of the 'parent' or 'child' in [0].

[0]: https://ljs.io/anon_vma_example.png

If we allow merges for those, then we propagate lock contention across the whole
of the merged VMA, because anon_vma locks are held across the entire fork
hierarchy, so we intentionally disallow those.

My change accidentally undid that in this one specific mremap() case, and
3b617fd3d317 fixed that.

But 61f67c230a5e is the issue here I think!

>
> But I am not sure if 879bca0a2c4f could have triggered that. Are you aware
> of other fixes that went into 6.19 that could have fixed such a scenario?

See above!

>
> --
> Cheers,
>
> David

Cheers, Lorenzo