[PATCH v2] xarray: clear the new head's parent before freeing the old head

From: Nickolai Zeldovich

Date: Tue Oct 27 2026 - 15:21:40 EST


xas_shrink() passes the old head to xa_node_free(), which queues it with
call_rcu(), before it clears the new head's parent pointer. A lockless
walker that loads the new head after the grace period has started can
still climb from it into the old head through xa_parent(), as
xas_find(), xas_find_marked(), xas_next() and xas_prev() do when they
step past the end of a node, and read the old head after it is freed.

Clear the parent pointer before freeing the old head, as
radix_tree_shrink() does.

With a delay injected after xa_node_free() in xas_shrink(), KASAN
reports a slab-use-after-free in the walker; with this patch it does
not.

Fixes: 58d6ea3085f2 ("xarray: Add XArray unconditional store operations")
---

Notes:
This patch was prepared with the help of an AI tool.

Changes in v2:
- Shorter commit message (Matthew Wilcox). No code change.

lib/xarray.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/lib/xarray.c b/lib/xarray.c
index bfe7bef80f34e..ae9d7fc68861c 100644
--- a/lib/xarray.c
+++ b/lib/xarray.c
@@ -471,11 +471,12 @@ static void xas_shrink(struct xa_state *xas)
if (!xa_is_node(entry))
RCU_INIT_POINTER(node->slots[0], XA_RETRY_ENTRY);
xas_update(xas, node);
+ if (xa_is_node(entry))
+ xa_to_node(entry)->parent = NULL;
xa_node_free(node);
if (!xa_is_node(entry))
break;
node = xa_to_node(entry);
- node->parent = NULL;
}
}


base-commit: 62c457528529cdcfa32bc52b331ceca10f3937b6
--
2.53.0