Commit c36d451a authored by Matthew Wilcox (Oracle)'s avatar Matthew Wilcox (Oracle)
Browse files

XArray: Fix xas_pause for large multi-index entries



Inspired by the recent Coverity report, I looked for other places where
the offset wasn't being converted to an unsigned long before being
shifted, and I found one in xas_pause() when the entry being paused is
of order >32.

Fixes: b803b428 ("xarray: Add XArray iterators")
Signed-off-by: default avatarMatthew Wilcox (Oracle) <willy@infradead.org>
Cc: stable@vger.kernel.org
parent bd40b17c
Loading
Loading
Loading
Loading
+37 −0
Original line number Diff line number Diff line
@@ -1156,6 +1156,42 @@ static noinline void check_find_entry(struct xarray *xa)
	XA_BUG_ON(xa, !xa_empty(xa));
}

static noinline void check_pause(struct xarray *xa)
{
	XA_STATE(xas, xa, 0);
	void *entry;
	unsigned int order;
	unsigned long index = 1;
	unsigned int count = 0;

	for (order = 0; order < order_limit; order++) {
		XA_BUG_ON(xa, xa_store_order(xa, index, order,
					xa_mk_index(index), GFP_KERNEL));
		index += 1UL << order;
	}

	rcu_read_lock();
	xas_for_each(&xas, entry, ULONG_MAX) {
		XA_BUG_ON(xa, entry != xa_mk_index(1UL << count));
		count++;
	}
	rcu_read_unlock();
	XA_BUG_ON(xa, count != order_limit);

	count = 0;
	xas_set(&xas, 0);
	rcu_read_lock();
	xas_for_each(&xas, entry, ULONG_MAX) {
		XA_BUG_ON(xa, entry != xa_mk_index(1UL << count));
		count++;
		xas_pause(&xas);
	}
	rcu_read_unlock();
	XA_BUG_ON(xa, count != order_limit);

	xa_destroy(xa);
}

static noinline void check_move_tiny(struct xarray *xa)
{
	XA_STATE(xas, xa, 0);
@@ -1664,6 +1700,7 @@ static int xarray_checks(void)
	check_xa_alloc();
	check_find(&array);
	check_find_entry(&array);
	check_pause(&array);
	check_account(&array);
	check_destroy(&array);
	check_move(&array);
+1 −1
Original line number Diff line number Diff line
@@ -970,7 +970,7 @@ void xas_pause(struct xa_state *xas)

	xas->xa_node = XAS_RESTART;
	if (node) {
		unsigned int offset = xas->xa_offset;
		unsigned long offset = xas->xa_offset;
		while (++offset < XA_CHUNK_SIZE) {
			if (!xa_is_sibling(xa_entry(xas->xa, node, offset)))
				break;