Commit 7404e32c authored by Andrew Morton's avatar Andrew Morton Committed by Jaroslav Kysela

[PATCH] handle overflows in radix_tree_gang_lookup()

Fix a radix-tree bug spotted by Vladimir Saveliev <vs@namesys.com>.

Each step in the radix tree spans six address bits.  So a height=6 tree
spans 36-bits worth of nodes.

On 32-bit machines radix_tree_gang_lookup() doesn't handle this right -
at the 12TB mark it wraps back to zero, and returns pages at quite
wrong indices.

The patch fixes all that up, and tidies a couple of things.

A user-space test harness was developed so that the code can be sanely
tested.  It is at

	http://www.zip.com.au/~akpm/linux/patches/stuff/rtth.tar.gz
parent 2134c937
...@@ -196,8 +196,7 @@ EXPORT_SYMBOL(radix_tree_lookup); ...@@ -196,8 +196,7 @@ EXPORT_SYMBOL(radix_tree_lookup);
static /* inline */ unsigned int static /* inline */ unsigned int
__lookup(struct radix_tree_root *root, void **results, unsigned long index, __lookup(struct radix_tree_root *root, void **results, unsigned long index,
unsigned int max_items, unsigned long *next_index, unsigned int max_items, unsigned long *next_index)
unsigned long max_index)
{ {
unsigned int nr_found = 0; unsigned int nr_found = 0;
unsigned int shift; unsigned int shift;
...@@ -209,23 +208,21 @@ __lookup(struct radix_tree_root *root, void **results, unsigned long index, ...@@ -209,23 +208,21 @@ __lookup(struct radix_tree_root *root, void **results, unsigned long index,
while (height > 0) { while (height > 0) {
unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK; unsigned long i = (index >> shift) & RADIX_TREE_MAP_MASK;
for ( ; i < RADIX_TREE_MAP_SIZE; i++) { for ( ; i < RADIX_TREE_MAP_SIZE; i++) {
if (slot->slots[i] != NULL) if (slot->slots[i] != NULL)
break; break;
index &= ~((1 << shift) - 1); index &= ~((1 << shift) - 1);
index += 1 << shift; index += 1 << shift;
if (index == 0)
goto out; /* 32-bit wraparound */
} }
if (i == RADIX_TREE_MAP_SIZE) if (i == RADIX_TREE_MAP_SIZE)
goto out; goto out;
height--; height--;
shift -= RADIX_TREE_MAP_SHIFT; if (height == 0) { /* Bottom level: grab some items */
if (height == 0) { unsigned long j = index & RADIX_TREE_MAP_MASK;
/* Bottom level: grab some items */
unsigned long j;
BUG_ON((shift + RADIX_TREE_MAP_SHIFT) != 0);
j = index & RADIX_TREE_MAP_MASK;
for ( ; j < RADIX_TREE_MAP_SIZE; j++) { for ( ; j < RADIX_TREE_MAP_SIZE; j++) {
index++; index++;
if (slot->slots[j]) { if (slot->slots[j]) {
...@@ -235,6 +232,7 @@ __lookup(struct radix_tree_root *root, void **results, unsigned long index, ...@@ -235,6 +232,7 @@ __lookup(struct radix_tree_root *root, void **results, unsigned long index,
} }
} }
} }
shift -= RADIX_TREE_MAP_SHIFT;
slot = slot->slots[i]; slot = slot->slots[i];
} }
out: out:
...@@ -281,9 +279,9 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results, ...@@ -281,9 +279,9 @@ radix_tree_gang_lookup(struct radix_tree_root *root, void **results,
if (cur_index > max_index) if (cur_index > max_index)
break; break;
nr_found = __lookup(root, results + ret, cur_index, nr_found = __lookup(root, results + ret, cur_index,
max_items - ret, &next_index, max_index); max_items - ret, &next_index);
ret += nr_found; ret += nr_found;
if (next_index == max_index) if (next_index == 0)
break; break;
cur_index = next_index; cur_index = next_index;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment