If the radix tree user attempted to insert a colliding entry with an
existing multiorder entry, then radix_tree_create() could encounter
a sibling entry when walking down the tree to look for a slot.
Use radix_tree_descend() to fix the problem, and add a test-case to make
sure the problem doesn't come back in future.

Signed-off-by: Matthew Wilcox <wi...@linux.intel.com>
Reviewed-by: Ross Zwisler <ross.zwis...@linux.intel.com>
---
 lib/radix-tree.c                      | 4 ++--
 tools/testing/radix-tree/multiorder.c | 5 +++++
 2 files changed, 7 insertions(+), 2 deletions(-)

diff --git a/lib/radix-tree.c b/lib/radix-tree.c
index d894654b5ecc..4291f344cb95 100644
--- a/lib/radix-tree.c
+++ b/lib/radix-tree.c
@@ -548,9 +548,9 @@ int __radix_tree_create(struct radix_tree_root *root, 
unsigned long index,
                /* Go a level down */
                height--;
                shift -= RADIX_TREE_MAP_SHIFT;
-               offset = (index >> shift) & RADIX_TREE_MAP_MASK;
                node = indirect_to_ptr(slot);
-               slot = node->slots[offset];
+               offset = (index >> shift) & RADIX_TREE_MAP_MASK;
+               offset = radix_tree_descend(node, &slot, offset);
        }
 
 #ifdef CONFIG_RADIX_TREE_MULTIORDER
diff --git a/tools/testing/radix-tree/multiorder.c 
b/tools/testing/radix-tree/multiorder.c
index de3d0e96f987..0229e6fb590f 100644
--- a/tools/testing/radix-tree/multiorder.c
+++ b/tools/testing/radix-tree/multiorder.c
@@ -135,6 +135,11 @@ static void multiorder_check(unsigned long index, int 
order)
                item_check_absent(&tree, i);
        for (i = max; i < 2*max; i++)
                item_check_absent(&tree, i);
+       for (i = min; i < max; i++) {
+               static void *entry = (void *)
+                                       (0xA0 | RADIX_TREE_EXCEPTIONAL_ENTRY);
+               assert(radix_tree_insert(&tree, i, entry) == -EEXIST);
+       }
 
        assert(item_delete(&tree, index) != 0);
 
-- 
2.8.0.rc3

Reply via email to