From: sashiko-bot@kernel.org
To: tao <tao.wangtao@honor.com>
Cc: damon@lists.linux.dev
Subject: Re: [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL
Date: Tue, 07 Jul 2026 09:41:18 +0000 [thread overview]
Message-ID: <20260707094119.28A7E1F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260707063308.29176-12-tao.wangtao@honor.com>
Thank you for your contribution! Sashiko AI review found 7 potential issue(s) to consider:
- [High] Use-After-Free of `mm_struct` in `vma_refcount_put()` due to dropped `mm` pin.
- [High] Per-VMA lock `vm_lock_seq` exclusion bypass in `trylock_leaf_vma_rmap()`.
- [High] Lockless speculative access of `anon_nod->mm` risks dereferencing a freed pointer.
- [High] Use-After-Free of `anon_vma` on `folio_lock_anon_vma_read()` error path.
- [High] `trylock_leaf_vma_rmap()` locks completely unrelated VMAs due to missing `anon_vma` validation.
- [High] TOCTOU race due to missing VMA bounds re-validation after acquiring the VMA read lock.
- [High] Valid zero-address VMAs silently fail the fast path and are ignored by rmap iteration.
--
--- Patch [11]: [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL ---
commit e9e971f63165144548b2891ce74a7cd4448a1490
Author: tao <tao.wangtao@honor.com>
mm: optimize rmap for ANON_VMA_FRACTAL with PVL
Leaf anon_nodes have no children, and a given address can belong to at
most one VMA. In this case, PVL can protect the rmap.
> diff --git a/mm/rmap.c b/mm/rmap.c
> index d3677515fae92..0967ba2d2a466 100644
> --- a/mm/rmap.c
> +++ b/mm/rmap.c
[ ... ]
> @@ -1035,9 +1037,48 @@ void anon_node_lock_rmap(struct anon_node *anon_nod)
>
> void anon_node_unlock_rmap(struct anon_node *anon_nod)
> {
> +#ifdef CONFIG_PER_VMA_LOCK
> + /* Corresponds to folio_lock_anon_vma_read(). */
> + if (anon_rmap_is_leaf_vma(anon_nod)) {
> + vma_refcount_put(anon_rmap_to_leaf_vma(anon_nod));
[Severity: High]
Can this cause a use-after-free crash by accessing an unpinned mm_struct?
In trylock_leaf_vma_rmap(), the original mm_struct is temporarily pinned
via mmget_not_zero(mm), but the pin is immediately dropped with
mmput_async(mm). It then checks vma->vm_mm == mm and sets other_mm = NULL.
The caller folio_lock_anon_vma_read() sees other_mm == NULL and returns
the tagged leaf VMA without holding any pin (via mmgrab()) on the
mm_struct. When the rmap operation completes, anon_node_unlock_rmap()
calls vma_refcount_put() here. Since the thread holds no reference to
the mm_struct, it can be freed concurrently. vma_refcount_put() then
accesses the unpinned mm_struct to call
rcuwait_wake_up(&vma->vm_mm->vma_writer_wait), which can crash.
> + return;
> + }
> +#endif
> +
> up_read(anon_node_rmap_sem(anon_nod));
> }
>
> +static struct vm_area_struct *trylock_leaf_vma_rmap(
> + struct anon_node *anon_nod, const struct folio *folio,
> + struct mm_struct **other_mm)
> +{
> + unsigned long pgoff_start = folio_pgoff(folio);
> + unsigned long pgoff_end = pgoff_start + folio_nr_pages(folio) - 1;
> + unsigned long addr = anon_node_rmap_address(anon_nod, pgoff_start);
> + struct mm_struct *mm = anon_nod->mm;
> + struct vm_area_struct *vma;
> + int oldcnt;
> +
> + if (anon_nod->nr_children)
> + return NULL;
> +
> + if (!addr || !mm || !mmget_not_zero(mm))
[Severity: High]
Is it safe to read anon_nod->mm locklessly under RCU?
The anon_node structure is allocated with SLAB_TYPESAFE_BY_RCU, meaning
the object itself can be freed and reused. However, the mm_struct it
points to is not RCU protected and can be completely freed and returned
to the allocator. Dereferencing the potentially stale mm pointer from
the SLAB-recycled anon_nod and passing it to mmget_not_zero() could
access unmapped memory or corrupt unrelated structures.
[Severity: High]
Does this unintentionally exclude valid VMAs mapped at address zero?
anon_node_rmap_address() can legitimately return 0 for an anonymous VMA
mapped at virtual address 0. Checking if (!addr) treats 0 as an error
rather than a valid address, which silently skips valid zero-address
VMAs for rmap walkers, hwpoison, and KSM.
> + return NULL;
> +
> + vma = vma_lookup(mm, addr);
> + mmput_async(mm);
> + if (!vma || vma_last_pgoff(vma) < pgoff_end)
[Severity: High]
Could this inadvertently lock a completely unrelated VMA?
This uses vma_lookup() to locate a VMA and returns it if it covers the
target offset, but fails to verify that the returned VMA's anon_vma
pointer matches the anon_nod, or even if the VMA is anonymous. If the
original VMA was unmapped and a completely unrelated VMA was mapped at
the exact same address, vma_lookup() will find it. This unrelated VMA
will be incorrectly locked and returned to the rmap walker, skipping
the true anon_vma tree and executing callbacks on the wrong VMA.
> + return NULL;
> +
> + if (!__refcount_inc_not_zero_limited_acquire(&vma->vm_refcnt, &oldcnt,
> + VM_REFCNT_LIMIT)) {
[Severity: High]
Does this bypass the required per-VMA lock vm_lock_seq exclusion?
The per-VMA lock algorithm strictly requires a reader to increment
vm_refcnt and then verify that vma->vm_lock_seq != mm->mm_lock_seq to
ensure no writer is currently locking the VMA. This successfully grants
the VMA read lock purely by incrementing vm_refcnt, failing to perform
any vm_lock_seq exclusion checks. A reader will incorrectly proceed even
if a writer holds the VMA lock, leading to potential data corruption as
the rmap walker accesses page tables concurrently with structural VMA
modifications.
[Severity: High]
Is there a time-of-check to time-of-use race here?
vma_last_pgoff(vma) < pgoff_end is checked locklessly under RCU just
before this. Between that check and the increment of vm_refcnt to
acquire the read lock, a concurrent writer could modify the VMA. Because
this function fails to re-check the VMA bounds after locking, the rmap
walker might receive a locked VMA that is too small and no longer covers
the required address range, resulting in out-of-bounds page table
accesses.
> + return NULL;
> + }
> +
> + *other_mm = (vma->vm_mm == mm) ? NULL : vma->vm_mm;
> + return vma;
> +}
[ ... ]
> @@ -1156,9 +1208,8 @@ struct anon_vma *folio_lock_anon_vma_read(const struct folio *folio,
> }
>
> if (!folio_mapped(folio)) {
> - rcu_read_unlock();
> put_anon_vma(anon_vma);
> - return NULL;
> + goto out;
[Severity: High]
Will this leave the caller with a freed anon_vma pointer?
In this fallback path, if down_read_trylock() fails and
!folio_mapped(folio) is true, the code calls put_anon_vma(anon_vma).
The original code correctly returned NULL at this point. By replacing
this with goto out, it falls through to return anon_vma without setting
anon_vma to NULL. The caller receives a non-NULL pointer, falsely
assuming the read lock was acquired, and will operate on the unpinned
and potentially freed anon_vma object.
> }
>
> /* we pinned the anon_vma, its safe to sleep */
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260707063308.29176-1-tao.wangtao@honor.com?part=11
next prev parent reply other threads:[~2026-07-07 9:41 UTC|newest]
Thread overview: 37+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-07 6:32 [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain tao
2026-07-07 6:32 ` [RFC PATCH v2 01/13] mm: add CONFIG_ANON_VMA_FRACTAL tao
2026-07-07 7:11 ` sashiko-bot
2026-07-07 6:32 ` [RFC PATCH v2 02/13] mm: implement helpers for ANON_VMA_FRACTAL tao
2026-07-07 7:23 ` sashiko-bot
2026-07-07 6:32 ` [RFC PATCH v2 03/13] mm: implement __anon_node_prepare " tao
2026-07-07 7:39 ` sashiko-bot
2026-07-07 6:32 ` [RFC PATCH v2 04/13] mm: implement anon_node_clone " tao
2026-07-07 8:00 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 05/13] mm: implement anon_node_fork_with_prev " tao
2026-07-07 8:22 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 06/13] mm: implement unlink_anon_nodes " tao
2026-07-07 6:33 ` [RFC PATCH v2 07/13] mm: handle rmap_base changes " tao
2026-07-07 8:45 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 08/13] mm: implement anonymous folio rmap " tao
2026-07-07 9:01 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 09/13] mm: prepare anon_node replacement " tao
2026-07-07 9:12 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 10/13] mm: replace anon_vma with anon_node " tao
2026-07-07 9:25 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 11/13] mm: optimize rmap for ANON_VMA_FRACTAL with PVL tao
2026-07-07 9:41 ` sashiko-bot [this message]
2026-07-07 6:33 ` [RFC PATCH v2 12/13] mm: shared semaphores for ANON_VMA_FRACTAL tao
2026-07-07 9:59 ` sashiko-bot
2026-07-07 6:33 ` [RFC PATCH v2 13/13] mm: Enable CONFIG_ANON_VMA_FRACTAL by default tao
2026-07-07 10:21 ` sashiko-bot
2026-07-07 7:01 ` [RFC PATCH v2 00/13] mm: rework anon_vma and remove anon_vma_chain David Hildenbrand (Arm)
2026-07-07 8:21 ` wangtao
2026-07-07 8:31 ` David Hildenbrand (Arm)
2026-07-07 9:20 ` wangtao
2026-07-07 16:19 ` Gregory Price
2026-07-07 17:09 ` David Hildenbrand (Arm)
2026-07-08 3:26 ` wangtao
2026-07-08 8:18 ` David Hildenbrand (Arm)
2026-07-07 9:07 ` Pedro Falcato
2026-07-07 15:27 ` Borislav Petkov
2026-07-07 21:32 ` [syzbot ci] " syzbot ci
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=20260707094119.28A7E1F000E9@smtp.kernel.org \
--to=sashiko-bot@kernel.org \
--cc=damon@lists.linux.dev \
--cc=sashiko-reviews@lists.linux.dev \
--cc=tao.wangtao@honor.com \
/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