idr: Fix handling of IDs above INT_MAX
Khalid reported that the kernel selftests are currently failing:
selftests: test_bpf.sh
========================================
test_bpf: [FAIL]
not ok 1..8 selftests: test_bpf.sh [FAIL]
He bisected it to 6ce711f275
("idr: Make
1-based IDRs more efficient").
The root cause is doing a signed comparison in idr_alloc_u32() instead
of an unsigned comparison. I went looking for any similar problems and
found a couple (which would each result in the failure to warn in two
situations that aren't supposed to happen).
I knocked up a few test-cases to prove that I was right and added them
to the test-suite.
Reported-by: Khalid Aziz <khalid.aziz@oracle.com>
Tested-by: Khalid Aziz <khalid.aziz@oracle.com>
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
This commit is contained in:
13
lib/idr.c
13
lib/idr.c
@@ -36,8 +36,8 @@ int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid,
|
||||
{
|
||||
struct radix_tree_iter iter;
|
||||
void __rcu **slot;
|
||||
int base = idr->idr_base;
|
||||
int id = *nextid;
|
||||
unsigned int base = idr->idr_base;
|
||||
unsigned int id = *nextid;
|
||||
|
||||
if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr)))
|
||||
return -EINVAL;
|
||||
@@ -204,10 +204,11 @@ int idr_for_each(const struct idr *idr,
|
||||
|
||||
radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, 0) {
|
||||
int ret;
|
||||
unsigned long id = iter.index + base;
|
||||
|
||||
if (WARN_ON_ONCE(iter.index > INT_MAX))
|
||||
if (WARN_ON_ONCE(id > INT_MAX))
|
||||
break;
|
||||
ret = fn(iter.index + base, rcu_dereference_raw(*slot), data);
|
||||
ret = fn(id, rcu_dereference_raw(*slot), data);
|
||||
if (ret)
|
||||
return ret;
|
||||
}
|
||||
@@ -230,8 +231,8 @@ void *idr_get_next(struct idr *idr, int *nextid)
|
||||
{
|
||||
struct radix_tree_iter iter;
|
||||
void __rcu **slot;
|
||||
int base = idr->idr_base;
|
||||
int id = *nextid;
|
||||
unsigned long base = idr->idr_base;
|
||||
unsigned long id = *nextid;
|
||||
|
||||
id = (id < base) ? 0 : id - base;
|
||||
slot = radix_tree_iter_find(&idr->idr_rt, &iter, id);
|
||||
|
Reference in New Issue
Block a user