maple_tree: fix mas_empty_area_rev() lower bound validation
authorLiam Howlett <liam.howlett@oracle.com>
Wed, 11 Jan 2023 20:02:07 +0000 (20:02 +0000)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 9 Feb 2023 10:28:23 +0000 (11:28 +0100)
commit 7327e8111adb315423035fb5233533016dfd3f2e upstream.

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: 54a611b60590 ("Maple Tree: add new data structure")
Signed-off-by: Liam R. Howlett <Liam.Howlett@oracle.com>
Reported-by: <amanieu@gmail.com>
Link: https://lore.kernel.org/linux-mm/0b9f5425-08d4-8013-aa4c-e620c3b10bb2@leemhuis.info/
Tested-by: Holger Hoffsttte <holger@applied-asynchrony.com>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
lib/maple_tree.c
lib/test_maple_tree.c

index 5bfaae6..69cb44b 100644 (file)
@@ -4883,7 +4883,7 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
        unsigned long *pivots, *gaps;
        void __rcu **slots;
        unsigned long gap = 0;
-       unsigned long max, min, index;
+       unsigned long max, min;
        unsigned char offset;
 
        if (unlikely(mas_is_err(mas)))
@@ -4905,8 +4905,7 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
                min = mas_safe_min(mas, pivots, --offset);
 
        max = mas_safe_pivot(mas, pivots, offset, type);
-       index = mas->index;
-       while (index <= max) {
+       while (mas->index <= max) {
                gap = 0;
                if (gaps)
                        gap = gaps[offset];
@@ -4937,10 +4936,8 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
                min = mas_safe_min(mas, pivots, offset);
        }
 
-       if (unlikely(index > max)) {
-               mas_set_err(mas, -EBUSY);
-               return false;
-       }
+       if (unlikely((mas->index > max) || (size - 1 > max - mas->index)))
+               goto no_space;
 
        if (unlikely(ma_is_leaf(type))) {
                mas->offset = offset;
@@ -4957,9 +4954,11 @@ static bool mas_rev_awalk(struct ma_state *mas, unsigned long size)
        return false;
 
 ascend:
-       if (mte_is_root(mas->node))
-               mas_set_err(mas, -EBUSY);
+       if (!mte_is_root(mas->node))
+               return false;
 
+no_space:
+       mas_set_err(mas, -EBUSY);
        return false;
 }
 
index 497fc93..ec847bf 100644 (file)
@@ -2517,6 +2517,91 @@ static noinline void check_bnode_min_spanning(struct maple_tree *mt)
        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 int maple_tree_seed(void)
 {
@@ -2765,6 +2850,10 @@ static int maple_tree_seed(void)
        check_bnode_min_spanning(&tree);
        mtree_destroy(&tree);
 
+       mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE);
+       check_empty_area_window(&tree);
+       mtree_destroy(&tree);
+
 #if defined(BENCH)
 skip:
 #endif