radix-tree: fix radix_tree_create for sibling entries
authorMatthew Wilcox <willy@linux.intel.com>
Sat, 21 May 2016 00:02:44 +0000 (17:02 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Sat, 21 May 2016 00:58:30 +0000 (17:58 -0700)
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 <willy@linux.intel.com>
Reviewed-by: Ross Zwisler <ross.zwisler@linux.intel.com>
Cc: Konstantin Khlebnikov <koct9i@gmail.com>
Cc: Kirill Shutemov <kirill.shutemov@linux.intel.com>
Cc: Jan Kara <jack@suse.com>
Cc: Neil Brown <neilb@suse.de>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
lib/radix-tree.c
tools/testing/radix-tree/multiorder.c

index b1ca744..9b5d8a9 100644 (file)
@@ -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
index 1b6fc9b..fc93457 100644 (file)
@@ -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);