diff options
author | Matthew Wilcox <mawilcox@microsoft.com> | 2018-02-26 14:39:30 -0500 |
---|---|---|
committer | Matthew Wilcox <mawilcox@microsoft.com> | 2018-02-26 14:39:30 -0500 |
commit | 4b0ad07653ee94182e2d8f21404242c9e83ad0b4 (patch) | |
tree | 88d581f08d2eac9d8b5d933c2740b91003e44401 | |
parent | 3d4d5d618639c3155cfce57101d619a0935434d2 (diff) |
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 6ce711f2750031d12cec91384ac5cfa0a485b60a ("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>
-rw-r--r-- | lib/idr.c | 13 | ||||
-rw-r--r-- | tools/testing/radix-tree/idr-test.c | 52 |
2 files changed, 59 insertions, 6 deletions
@@ -36,8 +36,8 @@ int idr_alloc_u32(struct idr *idr, void *ptr, u32 *nextid, | |||
36 | { | 36 | { |
37 | struct radix_tree_iter iter; | 37 | struct radix_tree_iter iter; |
38 | void __rcu **slot; | 38 | void __rcu **slot; |
39 | int base = idr->idr_base; | 39 | unsigned int base = idr->idr_base; |
40 | int id = *nextid; | 40 | unsigned int id = *nextid; |
41 | 41 | ||
42 | if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) | 42 | if (WARN_ON_ONCE(radix_tree_is_internal_node(ptr))) |
43 | return -EINVAL; | 43 | return -EINVAL; |
@@ -204,10 +204,11 @@ int idr_for_each(const struct idr *idr, | |||
204 | 204 | ||
205 | radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, 0) { | 205 | radix_tree_for_each_slot(slot, &idr->idr_rt, &iter, 0) { |
206 | int ret; | 206 | int ret; |
207 | unsigned long id = iter.index + base; | ||
207 | 208 | ||
208 | if (WARN_ON_ONCE(iter.index > INT_MAX)) | 209 | if (WARN_ON_ONCE(id > INT_MAX)) |
209 | break; | 210 | break; |
210 | ret = fn(iter.index + base, rcu_dereference_raw(*slot), data); | 211 | ret = fn(id, rcu_dereference_raw(*slot), data); |
211 | if (ret) | 212 | if (ret) |
212 | return ret; | 213 | return ret; |
213 | } | 214 | } |
@@ -230,8 +231,8 @@ void *idr_get_next(struct idr *idr, int *nextid) | |||
230 | { | 231 | { |
231 | struct radix_tree_iter iter; | 232 | struct radix_tree_iter iter; |
232 | void __rcu **slot; | 233 | void __rcu **slot; |
233 | int base = idr->idr_base; | 234 | unsigned long base = idr->idr_base; |
234 | int id = *nextid; | 235 | unsigned long id = *nextid; |
235 | 236 | ||
236 | id = (id < base) ? 0 : id - base; | 237 | id = (id < base) ? 0 : id - base; |
237 | slot = radix_tree_iter_find(&idr->idr_rt, &iter, id); | 238 | slot = radix_tree_iter_find(&idr->idr_rt, &iter, id); |
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c index 44ef9eba5a7a..6c645eb77d42 100644 --- a/tools/testing/radix-tree/idr-test.c +++ b/tools/testing/radix-tree/idr-test.c | |||
@@ -178,6 +178,55 @@ void idr_get_next_test(int base) | |||
178 | idr_destroy(&idr); | 178 | idr_destroy(&idr); |
179 | } | 179 | } |
180 | 180 | ||
181 | int idr_u32_cb(int id, void *ptr, void *data) | ||
182 | { | ||
183 | BUG_ON(id < 0); | ||
184 | BUG_ON(ptr != DUMMY_PTR); | ||
185 | return 0; | ||
186 | } | ||
187 | |||
188 | void idr_u32_test1(struct idr *idr, u32 handle) | ||
189 | { | ||
190 | static bool warned = false; | ||
191 | u32 id = handle; | ||
192 | int sid = 0; | ||
193 | void *ptr; | ||
194 | |||
195 | BUG_ON(idr_alloc_u32(idr, DUMMY_PTR, &id, id, GFP_KERNEL)); | ||
196 | BUG_ON(id != handle); | ||
197 | BUG_ON(idr_alloc_u32(idr, DUMMY_PTR, &id, id, GFP_KERNEL) != -ENOSPC); | ||
198 | BUG_ON(id != handle); | ||
199 | if (!warned && id > INT_MAX) | ||
200 | printk("vvv Ignore these warnings\n"); | ||
201 | ptr = idr_get_next(idr, &sid); | ||
202 | if (id > INT_MAX) { | ||
203 | BUG_ON(ptr != NULL); | ||
204 | BUG_ON(sid != 0); | ||
205 | } else { | ||
206 | BUG_ON(ptr != DUMMY_PTR); | ||
207 | BUG_ON(sid != id); | ||
208 | } | ||
209 | idr_for_each(idr, idr_u32_cb, NULL); | ||
210 | if (!warned && id > INT_MAX) { | ||
211 | printk("^^^ Warnings over\n"); | ||
212 | warned = true; | ||
213 | } | ||
214 | BUG_ON(idr_remove(idr, id) != DUMMY_PTR); | ||
215 | BUG_ON(!idr_is_empty(idr)); | ||
216 | } | ||
217 | |||
218 | void idr_u32_test(int base) | ||
219 | { | ||
220 | DEFINE_IDR(idr); | ||
221 | idr_init_base(&idr, base); | ||
222 | idr_u32_test1(&idr, 10); | ||
223 | idr_u32_test1(&idr, 0x7fffffff); | ||
224 | idr_u32_test1(&idr, 0x80000000); | ||
225 | idr_u32_test1(&idr, 0x80000001); | ||
226 | idr_u32_test1(&idr, 0xffe00000); | ||
227 | idr_u32_test1(&idr, 0xffffffff); | ||
228 | } | ||
229 | |||
181 | void idr_checks(void) | 230 | void idr_checks(void) |
182 | { | 231 | { |
183 | unsigned long i; | 232 | unsigned long i; |
@@ -248,6 +297,9 @@ void idr_checks(void) | |||
248 | idr_get_next_test(0); | 297 | idr_get_next_test(0); |
249 | idr_get_next_test(1); | 298 | idr_get_next_test(1); |
250 | idr_get_next_test(4); | 299 | idr_get_next_test(4); |
300 | idr_u32_test(4); | ||
301 | idr_u32_test(1); | ||
302 | idr_u32_test(0); | ||
251 | } | 303 | } |
252 | 304 | ||
253 | /* | 305 | /* |