diff options
author | Matthew Wilcox <mawilcox@microsoft.com> | 2017-03-03 12:16:10 -0500 |
---|---|---|
committer | Matthew Wilcox <mawilcox@microsoft.com> | 2017-03-07 13:18:23 -0500 |
commit | 4ecd9542dbc3e07f3bd3870aac12839f72b47db4 (patch) | |
tree | 1345c7ca57563006a0355bec1bbe689dd14ca67c /tools/testing/radix-tree/idr-test.c | |
parent | 3f1b6f9d49ba5a209d745fa2448657d8b66ed0c0 (diff) |
ida: Free correct IDA bitmap
There's a relatively rare race where we look at the per-cpu preallocated
IDA bitmap, see it's NULL, allocate a new one, and atomically update it.
If the kmalloc() happened to sleep and we were rescheduled to a different
CPU, or an interrupt came in at the exact right time, another task
might have successfully allocated a bitmap and already deposited it.
I forgot what the semantics of cmpxchg() were and ended up freeing the
wrong bitmap leading to KASAN reporting a use-after-free.
Dmitry found the bug with syzkaller & wrote the patch. I wrote the test
case that will reproduce the bug without his patch being applied.
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Matthew Wilcox <mawilcox@microsoft.com>
Diffstat (limited to 'tools/testing/radix-tree/idr-test.c')
-rw-r--r-- | tools/testing/radix-tree/idr-test.c | 34 |
1 files changed, 31 insertions, 3 deletions
diff --git a/tools/testing/radix-tree/idr-test.c b/tools/testing/radix-tree/idr-test.c index 86de901fa5c6..30cd0b296f1a 100644 --- a/tools/testing/radix-tree/idr-test.c +++ b/tools/testing/radix-tree/idr-test.c | |||
@@ -363,7 +363,7 @@ void ida_check_random(void) | |||
363 | { | 363 | { |
364 | DEFINE_IDA(ida); | 364 | DEFINE_IDA(ida); |
365 | DECLARE_BITMAP(bitmap, 2048); | 365 | DECLARE_BITMAP(bitmap, 2048); |
366 | int id; | 366 | int id, err; |
367 | unsigned int i; | 367 | unsigned int i; |
368 | time_t s = time(NULL); | 368 | time_t s = time(NULL); |
369 | 369 | ||
@@ -377,8 +377,11 @@ void ida_check_random(void) | |||
377 | ida_remove(&ida, bit); | 377 | ida_remove(&ida, bit); |
378 | } else { | 378 | } else { |
379 | __set_bit(bit, bitmap); | 379 | __set_bit(bit, bitmap); |
380 | ida_pre_get(&ida, GFP_KERNEL); | 380 | do { |
381 | assert(!ida_get_new_above(&ida, bit, &id)); | 381 | ida_pre_get(&ida, GFP_KERNEL); |
382 | err = ida_get_new_above(&ida, bit, &id); | ||
383 | } while (err == -ENOMEM); | ||
384 | assert(!err); | ||
382 | assert(id == bit); | 385 | assert(id == bit); |
383 | } | 386 | } |
384 | } | 387 | } |
@@ -476,11 +479,36 @@ void ida_checks(void) | |||
476 | radix_tree_cpu_dead(1); | 479 | radix_tree_cpu_dead(1); |
477 | } | 480 | } |
478 | 481 | ||
482 | static void *ida_random_fn(void *arg) | ||
483 | { | ||
484 | rcu_register_thread(); | ||
485 | ida_check_random(); | ||
486 | rcu_unregister_thread(); | ||
487 | return NULL; | ||
488 | } | ||
489 | |||
490 | void ida_thread_tests(void) | ||
491 | { | ||
492 | pthread_t threads[10]; | ||
493 | int i; | ||
494 | |||
495 | for (i = 0; i < ARRAY_SIZE(threads); i++) | ||
496 | if (pthread_create(&threads[i], NULL, ida_random_fn, NULL)) { | ||
497 | perror("creating ida thread"); | ||
498 | exit(1); | ||
499 | } | ||
500 | |||
501 | while (i--) | ||
502 | pthread_join(threads[i], NULL); | ||
503 | } | ||
504 | |||
479 | int __weak main(void) | 505 | int __weak main(void) |
480 | { | 506 | { |
481 | radix_tree_init(); | 507 | radix_tree_init(); |
482 | idr_checks(); | 508 | idr_checks(); |
483 | ida_checks(); | 509 | ida_checks(); |
510 | ida_thread_tests(); | ||
511 | radix_tree_cpu_dead(1); | ||
484 | rcu_barrier(); | 512 | rcu_barrier(); |
485 | if (nr_allocated) | 513 | if (nr_allocated) |
486 | printf("nr_allocated = %d\n", nr_allocated); | 514 | printf("nr_allocated = %d\n", nr_allocated); |