diff options
Diffstat (limited to 'mm/kasan/kasan.c')
-rw-r--r-- | mm/kasan/kasan.c | 108 |
1 files changed, 81 insertions, 27 deletions
diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index 405bba487df5..e13d911251e7 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c | |||
@@ -5,7 +5,7 @@ | |||
5 | * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> | 5 | * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> |
6 | * | 6 | * |
7 | * Some code borrowed from https://github.com/xairy/kasan-prototype by | 7 | * Some code borrowed from https://github.com/xairy/kasan-prototype by |
8 | * Andrey Konovalov <adech.fo@gmail.com> | 8 | * Andrey Konovalov <andreyknvl@gmail.com> |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 10 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License version 2 as | 11 | * it under the terms of the GNU General Public License version 2 as |
@@ -489,21 +489,17 @@ void kasan_slab_alloc(struct kmem_cache *cache, void *object, gfp_t flags) | |||
489 | kasan_kmalloc(cache, object, cache->object_size, flags); | 489 | kasan_kmalloc(cache, object, cache->object_size, flags); |
490 | } | 490 | } |
491 | 491 | ||
492 | static void kasan_poison_slab_free(struct kmem_cache *cache, void *object) | 492 | static bool __kasan_slab_free(struct kmem_cache *cache, void *object, |
493 | { | 493 | unsigned long ip, bool quarantine) |
494 | unsigned long size = cache->object_size; | ||
495 | unsigned long rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE); | ||
496 | |||
497 | /* RCU slabs could be legally used after free within the RCU period */ | ||
498 | if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU)) | ||
499 | return; | ||
500 | |||
501 | kasan_poison_shadow(object, rounded_up_size, KASAN_KMALLOC_FREE); | ||
502 | } | ||
503 | |||
504 | bool kasan_slab_free(struct kmem_cache *cache, void *object) | ||
505 | { | 494 | { |
506 | s8 shadow_byte; | 495 | s8 shadow_byte; |
496 | unsigned long rounded_up_size; | ||
497 | |||
498 | if (unlikely(nearest_obj(cache, virt_to_head_page(object), object) != | ||
499 | object)) { | ||
500 | kasan_report_invalid_free(object, ip); | ||
501 | return true; | ||
502 | } | ||
507 | 503 | ||
508 | /* RCU slabs could be legally used after free within the RCU period */ | 504 | /* RCU slabs could be legally used after free within the RCU period */ |
509 | if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU)) | 505 | if (unlikely(cache->flags & SLAB_TYPESAFE_BY_RCU)) |
@@ -511,14 +507,14 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object) | |||
511 | 507 | ||
512 | shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object)); | 508 | shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object)); |
513 | if (shadow_byte < 0 || shadow_byte >= KASAN_SHADOW_SCALE_SIZE) { | 509 | if (shadow_byte < 0 || shadow_byte >= KASAN_SHADOW_SCALE_SIZE) { |
514 | kasan_report_double_free(cache, object, | 510 | kasan_report_invalid_free(object, ip); |
515 | __builtin_return_address(1)); | ||
516 | return true; | 511 | return true; |
517 | } | 512 | } |
518 | 513 | ||
519 | kasan_poison_slab_free(cache, object); | 514 | rounded_up_size = round_up(cache->object_size, KASAN_SHADOW_SCALE_SIZE); |
515 | kasan_poison_shadow(object, rounded_up_size, KASAN_KMALLOC_FREE); | ||
520 | 516 | ||
521 | if (unlikely(!(cache->flags & SLAB_KASAN))) | 517 | if (!quarantine || unlikely(!(cache->flags & SLAB_KASAN))) |
522 | return false; | 518 | return false; |
523 | 519 | ||
524 | set_track(&get_alloc_info(cache, object)->free_track, GFP_NOWAIT); | 520 | set_track(&get_alloc_info(cache, object)->free_track, GFP_NOWAIT); |
@@ -526,6 +522,11 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object) | |||
526 | return true; | 522 | return true; |
527 | } | 523 | } |
528 | 524 | ||
525 | bool kasan_slab_free(struct kmem_cache *cache, void *object, unsigned long ip) | ||
526 | { | ||
527 | return __kasan_slab_free(cache, object, ip, true); | ||
528 | } | ||
529 | |||
529 | void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size, | 530 | void kasan_kmalloc(struct kmem_cache *cache, const void *object, size_t size, |
530 | gfp_t flags) | 531 | gfp_t flags) |
531 | { | 532 | { |
@@ -589,25 +590,29 @@ void kasan_krealloc(const void *object, size_t size, gfp_t flags) | |||
589 | kasan_kmalloc(page->slab_cache, object, size, flags); | 590 | kasan_kmalloc(page->slab_cache, object, size, flags); |
590 | } | 591 | } |
591 | 592 | ||
592 | void kasan_poison_kfree(void *ptr) | 593 | void kasan_poison_kfree(void *ptr, unsigned long ip) |
593 | { | 594 | { |
594 | struct page *page; | 595 | struct page *page; |
595 | 596 | ||
596 | page = virt_to_head_page(ptr); | 597 | page = virt_to_head_page(ptr); |
597 | 598 | ||
598 | if (unlikely(!PageSlab(page))) | 599 | if (unlikely(!PageSlab(page))) { |
600 | if (ptr != page_address(page)) { | ||
601 | kasan_report_invalid_free(ptr, ip); | ||
602 | return; | ||
603 | } | ||
599 | kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page), | 604 | kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page), |
600 | KASAN_FREE_PAGE); | 605 | KASAN_FREE_PAGE); |
601 | else | 606 | } else { |
602 | kasan_poison_slab_free(page->slab_cache, ptr); | 607 | __kasan_slab_free(page->slab_cache, ptr, ip, false); |
608 | } | ||
603 | } | 609 | } |
604 | 610 | ||
605 | void kasan_kfree_large(const void *ptr) | 611 | void kasan_kfree_large(void *ptr, unsigned long ip) |
606 | { | 612 | { |
607 | struct page *page = virt_to_page(ptr); | 613 | if (ptr != page_address(virt_to_head_page(ptr))) |
608 | 614 | kasan_report_invalid_free(ptr, ip); | |
609 | kasan_poison_shadow(ptr, PAGE_SIZE << compound_order(page), | 615 | /* The object will be poisoned by page_alloc. */ |
610 | KASAN_FREE_PAGE); | ||
611 | } | 616 | } |
612 | 617 | ||
613 | int kasan_module_alloc(void *addr, size_t size) | 618 | int kasan_module_alloc(void *addr, size_t size) |
@@ -736,6 +741,55 @@ void __asan_unpoison_stack_memory(const void *addr, size_t size) | |||
736 | } | 741 | } |
737 | EXPORT_SYMBOL(__asan_unpoison_stack_memory); | 742 | EXPORT_SYMBOL(__asan_unpoison_stack_memory); |
738 | 743 | ||
744 | /* Emitted by compiler to poison alloca()ed objects. */ | ||
745 | void __asan_alloca_poison(unsigned long addr, size_t size) | ||
746 | { | ||
747 | size_t rounded_up_size = round_up(size, KASAN_SHADOW_SCALE_SIZE); | ||
748 | size_t padding_size = round_up(size, KASAN_ALLOCA_REDZONE_SIZE) - | ||
749 | rounded_up_size; | ||
750 | size_t rounded_down_size = round_down(size, KASAN_SHADOW_SCALE_SIZE); | ||
751 | |||
752 | const void *left_redzone = (const void *)(addr - | ||
753 | KASAN_ALLOCA_REDZONE_SIZE); | ||
754 | const void *right_redzone = (const void *)(addr + rounded_up_size); | ||
755 | |||
756 | WARN_ON(!IS_ALIGNED(addr, KASAN_ALLOCA_REDZONE_SIZE)); | ||
757 | |||
758 | kasan_unpoison_shadow((const void *)(addr + rounded_down_size), | ||
759 | size - rounded_down_size); | ||
760 | kasan_poison_shadow(left_redzone, KASAN_ALLOCA_REDZONE_SIZE, | ||
761 | KASAN_ALLOCA_LEFT); | ||
762 | kasan_poison_shadow(right_redzone, | ||
763 | padding_size + KASAN_ALLOCA_REDZONE_SIZE, | ||
764 | KASAN_ALLOCA_RIGHT); | ||
765 | } | ||
766 | EXPORT_SYMBOL(__asan_alloca_poison); | ||
767 | |||
768 | /* Emitted by compiler to unpoison alloca()ed areas when the stack unwinds. */ | ||
769 | void __asan_allocas_unpoison(const void *stack_top, const void *stack_bottom) | ||
770 | { | ||
771 | if (unlikely(!stack_top || stack_top > stack_bottom)) | ||
772 | return; | ||
773 | |||
774 | kasan_unpoison_shadow(stack_top, stack_bottom - stack_top); | ||
775 | } | ||
776 | EXPORT_SYMBOL(__asan_allocas_unpoison); | ||
777 | |||
778 | /* Emitted by the compiler to [un]poison local variables. */ | ||
779 | #define DEFINE_ASAN_SET_SHADOW(byte) \ | ||
780 | void __asan_set_shadow_##byte(const void *addr, size_t size) \ | ||
781 | { \ | ||
782 | __memset((void *)addr, 0x##byte, size); \ | ||
783 | } \ | ||
784 | EXPORT_SYMBOL(__asan_set_shadow_##byte) | ||
785 | |||
786 | DEFINE_ASAN_SET_SHADOW(00); | ||
787 | DEFINE_ASAN_SET_SHADOW(f1); | ||
788 | DEFINE_ASAN_SET_SHADOW(f2); | ||
789 | DEFINE_ASAN_SET_SHADOW(f3); | ||
790 | DEFINE_ASAN_SET_SHADOW(f5); | ||
791 | DEFINE_ASAN_SET_SHADOW(f8); | ||
792 | |||
739 | #ifdef CONFIG_MEMORY_HOTPLUG | 793 | #ifdef CONFIG_MEMORY_HOTPLUG |
740 | static int __meminit kasan_mem_notifier(struct notifier_block *nb, | 794 | static int __meminit kasan_mem_notifier(struct notifier_block *nb, |
741 | unsigned long action, void *data) | 795 | unsigned long action, void *data) |