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

XArray: Fix xa_for_each with a single element at 0

The following sequence of calls would result in an infinite loop in
xa_find_after():

xa_store(xa, 0, x, GFP_KERNEL);
index = 0;
xa_for_each(xa, entry, index, ULONG_MAX, XA_PRESENT) { }

xa_find_after() was confusing the situation where we found no entry in
the tree with finding a multiorder entry, so it would look for the
successor entry forever. Just check for this case explicitly. Includes
a few new checks in the test suite to be sure this doesn't reappear.

Signed-off-by: Matthew Wilcox <willy@infradead.org>

+31 -1
+29 -1
lib/test_xarray.c
··· 702 702 } 703 703 } 704 704 705 - static noinline void check_find(struct xarray *xa) 705 + static noinline void check_find_1(struct xarray *xa) 706 706 { 707 707 unsigned long i, j, k; 708 708 ··· 748 748 XA_BUG_ON(xa, xa_get_mark(xa, i, XA_MARK_0)); 749 749 } 750 750 XA_BUG_ON(xa, !xa_empty(xa)); 751 + } 752 + 753 + static noinline void check_find_2(struct xarray *xa) 754 + { 755 + void *entry; 756 + unsigned long i, j, index = 0; 757 + 758 + xa_for_each(xa, entry, index, ULONG_MAX, XA_PRESENT) { 759 + XA_BUG_ON(xa, true); 760 + } 761 + 762 + for (i = 0; i < 1024; i++) { 763 + xa_store_index(xa, index, GFP_KERNEL); 764 + j = 0; 765 + index = 0; 766 + xa_for_each(xa, entry, index, ULONG_MAX, XA_PRESENT) { 767 + XA_BUG_ON(xa, xa_mk_value(index) != entry); 768 + XA_BUG_ON(xa, index != j++); 769 + } 770 + } 771 + 772 + xa_destroy(xa); 773 + } 774 + 775 + static noinline void check_find(struct xarray *xa) 776 + { 777 + check_find_1(xa); 778 + check_find_2(xa); 751 779 check_multi_find(xa); 752 780 check_multi_find_2(xa); 753 781 }
+2
lib/xarray.c
··· 1829 1829 entry = xas_find_marked(&xas, max, filter); 1830 1830 else 1831 1831 entry = xas_find(&xas, max); 1832 + if (xas.xa_node == XAS_BOUNDS) 1833 + break; 1832 1834 if (xas.xa_shift) { 1833 1835 if (xas.xa_index & ((1UL << xas.xa_shift) - 1)) 1834 1836 continue;