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: 41+ 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-08 4:37 ` Hillf Danton
2026-07-08 7:45 ` David Hildenbrand (Arm)
2026-07-08 9:15 ` Hillf Danton
2026-07-08 11:05 ` David Hildenbrand (Arm)
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 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.