All of lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: tao <tao.wangtao@honor.com>
Cc: damon@lists.linux.dev
Subject: Re: [RFC PATCH v2 04/13] mm: implement anon_node_clone for ANON_VMA_FRACTAL
Date: Tue, 07 Jul 2026 08:00:28 +0000	[thread overview]
Message-ID: <20260707080028.C22E31F000E9@smtp.kernel.org> (raw)
In-Reply-To: <20260707063308.29176-5-tao.wangtao@honor.com>

Thank you for your contribution! Sashiko AI review found 8 potential issue(s) to consider:
- [Critical] Flattening the fractal tree into a single list corrupts child iteration when children have their own descendants, permanently severing the tree structure.
- [Critical] VMA unlinking unconditionally casts anon_node to anon_vma and overwrites mismatched fields.
- [High] Unsynchronized read-modify-write of root->depth causes data races and structural corruption of the fractal tree.
- [High] Newly allocated anon_node objects are permanently leaked because their reference counts are never decremented during VMA teardown.
- [High] anon_node objects are never removed from fractal_list, leading to a Kernel BUG and Use-After-Free.
- [High] Intermediate anon_node structures can be freed while they still have children in their fractal_list, leading to a VM_BUG_ON panic or Use-After-Free.
- [High] anon_node slab objects allocated in anon_node_track_rmap() with is_fork=false are permanently leaked because ownership is never transferred to a VMA.
- [Medium] Missing state re-validation after lock upgrade in anon_node_track_rmap() leads to duplicate child node insertions.
--

--- Patch [4]: [RFC PATCH v2 04/13] mm: implement anon_node_clone for ANON_VMA_FRACTAL ---
commit d6053627649639aa9d6f587172befdbc0e783aed
Author: tao <tao.wangtao@honor.com>

mm: implement anon_node_clone for ANON_VMA_FRACTAL

This commit implements anon_node_clone for ANON_VMA_FRACTAL. It creates
a child anon_node when the VMA is remapped or when the VMA count exceeds
the maximum trackable limit. Newly created nodes are inserted at the tail
of the rbc children list.

> diff --git a/mm/rmap.c b/mm/rmap.c
> index 55074ee931967..9dc82e7438b00 100644
> --- a/mm/rmap.c
> +++ b/mm/rmap.c

[ ... ]

> +static inline void vma_set_anon_node(struct vm_area_struct *vma,
> +		struct anon_node *anon_nod, bool is_locked)
> +{
> +	struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
> +
> +	if (is_locked) {
> +		vma->anon_vma = (struct anon_vma *)anon_nod;
> +		return;
> +	}
> +	down_write(rmap_sem);
> +	vma->anon_vma = (struct anon_vma *)anon_nod;

[Severity: Critical]
Does this type cast corrupt the anon_node structure during teardown?

During teardown, the standard unlink_anon_vmas() flow operates on this pointer
and executes active_anon_vma->num_active_vmas--. Since anon_node has a
different layout and lacks num_active_vmas, won't this unconditionally write
out of bounds and corrupt the anon_node fields?

> +	up_write(rmap_sem);
> +}
> +
> +static int anon_node_add_child(struct anon_node *anon_nod,
> +	struct vm_area_struct *vma, bool is_fork)
> +{
> +	struct anon_node *child;
> +	struct anon_node *root = anon_nod->root;
> +	struct rw_semaphore *rmap_sem = anon_node_rmap_sem(root);
> +	struct anon_node *rbc_ch = anon_nod, *next;
> +
> +	if (root->depth == 0)
> +		root->depth = 1;

[Severity: High]
Can this unsynchronized read-modify-write cause data races?

root->depth is being modified outside of any locks. Could a concurrent thread
allocating a node via anon_node_alloc() read an incorrect parent->depth of 0
instead of 1? Because tree traversals require accurate depths, won't an
incorrectly initialized branch permanently break the tree structure?

> +	child = anon_node_alloc(vma, anon_nod, is_fork);

[Severity: High]
Are these newly allocated anon_node objects permanently leaked?

anon_node is allocated with a refcount of 1. During VMA teardown,
unlink_anon_vmas() relies on iterating vma->anon_vma_chain to call
put_anon_vma(). However, since anon_node logic never populates anon_vma_chain,
won't the iteration loop silently skip over it and leak the memory?

[Severity: High]
Does anon_node_alloc() fail to pin the direct parent node?

In anon_node_alloc(), the logic increments the refcount of the root node but
doesn't increment the refcount of the direct parent. If an intermediate parent
node is unmapped, won't its refcount drop to 0 and get freed while it still
has children in its fractal_list? This could lead to a VM_BUG_ON panic or a
use-after-free for the children.

> +	if (!child)
> +		return -ENOMEM;
> +
> +	rmap_sem = anon_node_rmap_sem(root);
> +	down_write(rmap_sem);
> +	/* Add new child to the tail of rbc children. */
> +	while ((next = anon_node_next_rbc_child(anon_nod, rbc_ch)) != NULL) {
> +		rbc_ch = next;
> +	}
> +	list_add(&child->fractal_list, &rbc_ch->fractal_list);

[Severity: Critical]
Does inserting the new child here corrupt the contiguous list iteration?

The iterator macro anon_node_next_rbc_child() strictly expects contiguous
children and returns NULL when node->depth != parent->depth + 1:

mm/internal.h:anon_node_next_rbc_child() {
    ...
    return node->depth == anon_nod->depth + 1 ? node : NULL;
}

If an existing child has its own descendants, those descendants interrupt the
contiguous sequence. The loop above stops prematurely at the first child with
descendants. By inserting the new child via list_add() immediately after that
child, won't the new sibling be placed before the descendants? This would
break the depth check for both anon_node_next_rbc_child() and
anon_node_next_descendant(), permanently severing all subsequent subtrees.

[Severity: High]
Are anon_node objects ever removed from fractal_list during teardown?

Nodes are added to the list here, but it doesn't appear there is a
corresponding list_del() anywhere in the teardown logic. If anon_node_free()
were reached, wouldn't it assert VM_BUG_ON(!list_empty(&anon_nod->fractal_list))?
Furthermore, freeing a node without removing it from the list would leave
dangling list pointers in its parent and siblings, causing a use-after-free
on the next tree traversal.

> +	++anon_nod->nr_children;
> +
> +	if (is_fork)
> +		vma_set_anon_node(vma, child, true);

[Severity: High]
Are anon_node objects allocated with is_fork=false permanently leaked?

When anon_node_track_rmap() expands tracking by calling anon_node_add_child()
with is_fork=false, the vma_set_anon_node() call here is skipped. Because the
newly allocated child node is never attached to a VMA, is there any mechanism
to track or decrement its initial reference count when the VMA is ultimately
unmapped?

> +	up_write(rmap_sem);
> +	return 0;
> +}
> +
> +/* Returns 0 on success, non-zero on failure. */
> +static int anon_node_track_rmap(struct anon_node *anon_nod,
> +		struct vm_area_struct *vma)
> +{
> +	struct rw_semaphore *rmap_sem = anon_node_rmap_sem(anon_nod);
> +	unsigned long rmap_base = vma_rmap_base(vma);
> +	struct anon_node *rbc_ch = anon_nod;
> +
> +	if (try_track_rmap(anon_nod, rmap_base))
> +		return 0;
> +
> +	down_read(rmap_sem);
> +	while ((rbc_ch = anon_node_next_rbc_child(anon_nod, rbc_ch)) != NULL) {
> +		if (try_track_rmap(rbc_ch, rmap_base))
> +			break;
> +	}
> +	up_read(rmap_sem);
> +	if (rbc_ch)
> +		return 0;
> +
> +	return anon_node_add_child(anon_nod, vma, false);

[Severity: Medium]
Should the state be re-validated after acquiring the write lock?

anon_node_track_rmap() searches for a capable child node under a read lock.
If it fails, it drops the read lock and calls anon_node_add_child(), which
takes a write lock to unconditionally allocate and append a node.

Could concurrent threads that fail the read check queue up on the write lock
and blindly append redundant tracking nodes for the exact same rmap_base?

> +}

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260707063308.29176-1-tao.wangtao@honor.com?part=4

  reply	other threads:[~2026-07-07  8:00 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 [this message]
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
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=20260707080028.C22E31F000E9@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.