Commit 7327e811 authored by Liam Howlett's avatar Liam Howlett Committed by Andrew Morton
Browse files

maple_tree: fix mas_empty_area_rev() lower bound validation

mas_empty_area_rev() was not correctly validating the start of a gap
against the lower limit.  This could lead to the range starting lower than
the requested minimum.

Fix the issue by better validating a gap once one is found.

This commit also adds tests to the maple tree test suite for this issue
and tests the mas_empty_area() function for similar bound checking.

Link: https://lkml.kernel.org/r/20230111200136.1851322-1-Liam.Howlett@oracle.com
Link: https://bugzilla.kernel.org/show_bug.cgi?id=216911


Fixes: 54a611b6 ("Maple Tree: add new data structure")
Signed-off-by: default avatarLiam R. Howlett <Liam.Howlett@oracle.com>
Reported-by: default avatar <amanieu@gmail.com>
  Link: https://lore.kernel.org/linux-mm/0b9f5425-08d4-8013-aa4c-e620c3b10bb2@leemhuis.info/


Tested-by: default avatarHolger Hoffsttte <holger@applied-asynchrony.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
parent 24b5308c
Loading
Loading
Loading
Loading
+8 −9
Original line number Original line Diff line number Diff line
@@ -4887,7 +4887,7 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
	unsigned long *pivots, *gaps;
	unsigned long *pivots, *gaps;
	void __rcu **slots;
	void __rcu **slots;
	unsigned long gap = 0;
	unsigned long gap = 0;
	unsigned long max, min, index;
	unsigned long max, min;
	unsigned char offset;
	unsigned char offset;


	if (unlikely(mas_is_err(mas)))
	if (unlikely(mas_is_err(mas)))
@@ -4909,8 +4909,7 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
		min = mas_safe_min(mas, pivots, --offset);
		min = mas_safe_min(mas, pivots, --offset);


	max = mas_safe_pivot(mas, pivots, offset, type);
	max = mas_safe_pivot(mas, pivots, offset, type);
	index = mas->index;
	while (mas->index <= max) {
	while (index <= max) {
		gap = 0;
		gap = 0;
		if (gaps)
		if (gaps)
			gap = gaps[offset];
			gap = gaps[offset];
@@ -4941,10 +4940,8 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
		min = mas_safe_min(mas, pivots, offset);
		min = mas_safe_min(mas, pivots, offset);
	}
	}


	if (unlikely(index > max)) {
	if (unlikely((mas->index > max) || (size - 1 > max - mas->index)))
		mas_set_err(mas, -EBUSY);
		goto no_space;
		return false;
	}


	if (unlikely(ma_is_leaf(type))) {
	if (unlikely(ma_is_leaf(type))) {
		mas->offset = offset;
		mas->offset = offset;
@@ -4961,9 +4958,11 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
	return false;
	return false;


ascend:
ascend:
	if (mte_is_root(mas->node))
	if (!mte_is_root(mas->node))
		mas_set_err(mas, -EBUSY);
		return false;


no_space:
	mas_set_err(mas, -EBUSY);
	return false;
	return false;
}
}


+89 −0
Original line number Original line Diff line number Diff line
@@ -2517,6 +2517,91 @@ static noinline void check_bnode_min_spanning(struct maple_tree *mt)
	mt_set_non_kernel(0);
	mt_set_non_kernel(0);
}
}


static noinline void check_empty_area_window(struct maple_tree *mt)
{
	unsigned long i, nr_entries = 20;
	MA_STATE(mas, mt, 0, 0);

	for (i = 1; i <= nr_entries; i++)
		mtree_store_range(mt, i*10, i*10 + 9,
				  xa_mk_value(i), GFP_KERNEL);

	/* Create another hole besides the one at 0 */
	mtree_store_range(mt, 160, 169, NULL, GFP_KERNEL);

	/* Check lower bounds that don't fit */
	rcu_read_lock();
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 5, 90, 10) != -EBUSY);

	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 6, 90, 5) != -EBUSY);

	/* Check lower bound that does fit */
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 5, 90, 5) != 0);
	MT_BUG_ON(mt, mas.index != 5);
	MT_BUG_ON(mt, mas.last != 9);
	rcu_read_unlock();

	/* Check one gap that doesn't fit and one that does */
	rcu_read_lock();
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 5, 217, 9) != 0);
	MT_BUG_ON(mt, mas.index != 161);
	MT_BUG_ON(mt, mas.last != 169);

	/* Check one gap that does fit above the min */
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 100, 218, 3) != 0);
	MT_BUG_ON(mt, mas.index != 216);
	MT_BUG_ON(mt, mas.last != 218);

	/* Check size that doesn't fit any gap */
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 100, 218, 16) != -EBUSY);

	/*
	 * Check size that doesn't fit the lower end of the window but
	 * does fit the gap
	 */
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 167, 200, 4) != -EBUSY);

	/*
	 * Check size that doesn't fit the upper end of the window but
	 * does fit the gap
	 */
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area_rev(&mas, 100, 162, 4) != -EBUSY);

	/* Check mas_empty_area forward */
	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area(&mas, 0, 100, 9) != 0);
	MT_BUG_ON(mt, mas.index != 0);
	MT_BUG_ON(mt, mas.last != 8);

	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area(&mas, 0, 100, 4) != 0);
	MT_BUG_ON(mt, mas.index != 0);
	MT_BUG_ON(mt, mas.last != 3);

	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area(&mas, 0, 100, 11) != -EBUSY);

	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area(&mas, 5, 100, 6) != -EBUSY);

	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area(&mas, 0, 8, 10) != -EBUSY);

	mas_reset(&mas);
	mas_empty_area(&mas, 100, 165, 3);

	mas_reset(&mas);
	MT_BUG_ON(mt, mas_empty_area(&mas, 100, 163, 6) != -EBUSY);
	rcu_read_unlock();
}

static DEFINE_MTREE(tree);
static DEFINE_MTREE(tree);
static int maple_tree_seed(void)
static int maple_tree_seed(void)
{
{
@@ -2765,6 +2850,10 @@ static int maple_tree_seed(void)
	check_bnode_min_spanning(&tree);
	check_bnode_min_spanning(&tree);
	mtree_destroy(&tree);
	mtree_destroy(&tree);


	mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE);
	check_empty_area_window(&tree);
	mtree_destroy(&tree);

#if defined(BENCH)
#if defined(BENCH)
skip:
skip:
#endif
#endif