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