Re: [PATCH RFC] XArray: fix index jumping backwards in xas_find()

Krystian Kaniewski <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
Resynchronize a non-leaf `xas_find()` cursor to the node slot containing
`xa_index` before the existing `xas_next_offset()` call. The current
`get_offset(xa_index - 1, node) + 1` assignment already moves past the
resynchronization slot, then the common increment moves once more. For an
order-7 entry covering indexes 0 through 127, an adjacent entry at 128, 
and a
walk beginning at 100, the current code changes the offset from 0 to 2 
and then
to 3. It skips the stable entry at offset 2. This violates the documented
forward-iteration contract without any concurrent modification.

Keep the shift-aware mismatch detection and the XArray-layer repair. Use the
slot containing `xa_index` as the non-leaf resynchronization point, then 
let the
existing helper perform the single forward step. Preserve the 
established leaf
boundary calculation. Add deterministic coverage for the order-7 adjacent
entry case and for splitting a multi-index entry after a lookup begins 
inside
it. Implement both cases in a new `check_multi_find_4()` helper in
`lib/test_xarray.c` and invoke it from `check_find()`.

Correct the commit message to describe the retained KASAN use-after-free in
`filemap_map_pages()` through `ptep_get()`, not a slab-out-of-bounds report.
Preserve the verified backward-index mechanism, the `Fixes` tag, syzbot 
links,
and the existing Gemini provenance tag. Do not change locking, public 
APIs, or
caller error behavior.

On 8/17/2026 3:04 AM, syzbot wrote:
> A bug in the XArray iterator xas_find() causes the iterator's index
> (xas->xa_index) to jump backwards when iterating over a multi-index entry
> (like a THP) that resides in a non-leaf node and is concurrently split.
>
> When iterating over a multi-index entry in a non-leaf node, xas_load() sets
> xas->xa_offset to the base offset of the entry, but leaves xas->xa_index at
> the requested index. When the caller subsequently wants to advance to the
> next entry, xas_find() is called. xas_find() attempts to synchronize
> xas->xa_offset with xas->xa_index before advancing. However, the fixup
> logic was incorrectly restricted to leaf nodes (!xas->xa_node->shift).
> Because the THP resides in a non-leaf node, the fixup is skipped.
>
> As a result, xas_find() simply increments xas->xa_offset and recalculates
> xas->xa_index based on this new offset. This causes xas->xa_index to jump
> backwards. If the THP was concurrently split, the entry at the new offset
> is a node pointer, so xas_find() descends into it and returns the folio at
> the backwards index. The caller (filemap_map_pages()) then calculates the
> PTE pointer based on this backwards index, resulting in an out-of-bounds
> memory access.
>
> For example, a KASAN slab-out-of-bounds read can be triggered in ptep_get()
> via filemap_map_pages():
>
> BUG: KASAN: slab-out-of-bounds in ptep_get include/linux/pgtable.h:495
> [inline]
> BUG: KASAN: slab-out-of-bounds in filemap_map_folio_range mm/filemap.c:3820
> [inline]
> BUG: KASAN: slab-out-of-bounds in filemap_map_pages+0x1100/0x2090
> mm/filemap.c:3955
> Read of size 8 at addr ffff88811b3b4a00 by task syz-executor411/5876
>
> To fix this, remove the !xas->xa_node->shift restriction and use the
> get_offset() helper to correctly calculate the offset for any node shift.
> This ensures that xas->xa_offset is correctly synchronized with
> xas->xa_index before advancing, regardless of the node's depth in the tree.
>
> Fixes: b803b42823d0 ("xarray: Add XArray iterators")
> Assisted-by: Gemini:gemini-3.6-flash Gemini:gemini-3.1-pro-preview syzbot
> Reported-by: [email protected]
> Closes: https://syzkaller.appspot.com/bug?extid=b72767277f29b6407083
> Link: https://syzkaller.appspot.com/ai_job?id=608570d2-1765-472d-a79b-ea5b0b3a226a
> To: "Andrew Morton" <[email protected]>
> To: <[email protected]>
> To: <[email protected]>
> To: "Matthew Wilcox" <[email protected]>
> Cc: <[email protected]>
>
> ---
> diff --git a/lib/xarray.c b/lib/xarray.c
> index 9a8b49165..5695618bf 100644
> --- a/lib/xarray.c
> +++ b/lib/xarray.c
> @@ -1406,9 +1406,8 @@ void *xas_find(struct xa_state *xas, unsigned long max)
>   		entry = xas_load(xas);
>   		if (entry || xas_not_node(xas->xa_node))
>   			return entry;
> -	} else if (!xas->xa_node->shift &&
> -		    xas->xa_offset != (xas->xa_index & XA_CHUNK_MASK)) {
> -		xas->xa_offset = ((xas->xa_index - 1) & XA_CHUNK_MASK) + 1;
> +	} else if (xas->xa_offset != get_offset(xas->xa_index, xas->xa_node)) {
> +		xas->xa_offset = get_offset(xas->xa_index - 1, xas->xa_node) + 1;
>   	}
>   
>   	xas_next_offset(xas);
>
>
> base-commit: 8d3ae59288f1e7d58d76558a6ee96d533bc5019f
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.