Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

radix tree: Don't return retry entries from lookup

Commit 66ee620f06f9 ("idr: Permit any valid kernel pointer to be stored")
changed the radix tree lookup so that it stops when reaching the bottom
of the tree. However, the condition was added in the wrong place,
making it possible to return retry entries to the caller. Reorder the
tests to check for the retry entry before checking whether we're at the
bottom of the tree. The retry entry should never be found in the tree
root, so it's safe to defer the check until the end of the loop.

Add a regression test to the test-suite to be sure this doesn't come
back.

Fixes: 66ee620f06f9 ("idr: Permit any valid kernel pointer to be stored")
Reported-by: Greg Kurz <groug@kaod.org>
Signed-off-by: Matthew Wilcox <willy@infradead.org>

+84 -2
+2 -2
lib/radix-tree.c
··· 784 784 while (radix_tree_is_internal_node(node)) { 785 785 unsigned offset; 786 786 787 - if (node == RADIX_TREE_RETRY) 788 - goto restart; 789 787 parent = entry_to_node(node); 790 788 offset = radix_tree_descend(parent, &node, index); 791 789 slot = parent->slots + offset; 790 + if (node == RADIX_TREE_RETRY) 791 + goto restart; 792 792 if (parent->shift == 0) 793 793 break; 794 794 }
+1
tools/testing/radix-tree/Makefile
··· 7 7 TARGETS = main idr-test multiorder xarray 8 8 CORE_OFILES := xarray.o radix-tree.o idr.o linux.o test.o find_bit.o bitmap.o 9 9 OFILES = main.o $(CORE_OFILES) regression1.o regression2.o regression3.o \ 10 + regression4.o \ 10 11 tag_check.o multiorder.o idr-test.o iteration_check.o benchmark.o 11 12 12 13 ifndef SHIFT
+1
tools/testing/radix-tree/main.c
··· 308 308 regression1_test(); 309 309 regression2_test(); 310 310 regression3_test(); 311 + regression4_test(); 311 312 iteration_test(0, 10 + 90 * long_run); 312 313 iteration_test(7, 10 + 90 * long_run); 313 314 single_thread_tests(long_run);
+1
tools/testing/radix-tree/regression.h
··· 5 5 void regression1_test(void); 6 6 void regression2_test(void); 7 7 void regression3_test(void); 8 + void regression4_test(void); 8 9 9 10 #endif
+79
tools/testing/radix-tree/regression4.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/kernel.h> 3 + #include <linux/gfp.h> 4 + #include <linux/slab.h> 5 + #include <linux/radix-tree.h> 6 + #include <linux/rcupdate.h> 7 + #include <stdlib.h> 8 + #include <pthread.h> 9 + #include <stdio.h> 10 + #include <assert.h> 11 + 12 + #include "regression.h" 13 + 14 + static pthread_barrier_t worker_barrier; 15 + static int obj0, obj1; 16 + static RADIX_TREE(mt_tree, GFP_KERNEL); 17 + 18 + static void *reader_fn(void *arg) 19 + { 20 + int i; 21 + void *entry; 22 + 23 + rcu_register_thread(); 24 + pthread_barrier_wait(&worker_barrier); 25 + 26 + for (i = 0; i < 1000000; i++) { 27 + rcu_read_lock(); 28 + entry = radix_tree_lookup(&mt_tree, 0); 29 + rcu_read_unlock(); 30 + if (entry != &obj0) { 31 + printf("iteration %d bad entry = %p\n", i, entry); 32 + abort(); 33 + } 34 + } 35 + 36 + rcu_unregister_thread(); 37 + 38 + return NULL; 39 + } 40 + 41 + static void *writer_fn(void *arg) 42 + { 43 + int i; 44 + 45 + rcu_register_thread(); 46 + pthread_barrier_wait(&worker_barrier); 47 + 48 + for (i = 0; i < 1000000; i++) { 49 + radix_tree_insert(&mt_tree, 1, &obj1); 50 + radix_tree_delete(&mt_tree, 1); 51 + } 52 + 53 + rcu_unregister_thread(); 54 + 55 + return NULL; 56 + } 57 + 58 + void regression4_test(void) 59 + { 60 + pthread_t reader, writer; 61 + 62 + printv(1, "regression test 4 starting\n"); 63 + 64 + radix_tree_insert(&mt_tree, 0, &obj0); 65 + pthread_barrier_init(&worker_barrier, NULL, 2); 66 + 67 + if (pthread_create(&reader, NULL, reader_fn, NULL) || 68 + pthread_create(&writer, NULL, writer_fn, NULL)) { 69 + perror("pthread_create"); 70 + exit(1); 71 + } 72 + 73 + if (pthread_join(reader, NULL) || pthread_join(writer, NULL)) { 74 + perror("pthread_join"); 75 + exit(1); 76 + } 77 + 78 + printv(1, "regression test 4 passed\n"); 79 + }