kasan: separate double free case from invalid free
authorKuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
Wed, 15 Jun 2022 06:22:18 +0000 (14:22 +0800)
committerakpm <akpm@linux-foundation.org>
Mon, 18 Jul 2022 00:14:33 +0000 (17:14 -0700)
Currently, KASAN describes all invalid-free/double-free bugs as
"double-free or invalid-free".  This is ambiguous.

KASAN should report "double-free" when a double-free is a more likely
cause (the address points to the start of an object) and report
"invalid-free" otherwise [1].

[1] https://bugzilla.kernel.org/show_bug.cgi?id=212193

Link: https://lkml.kernel.org/r/20220615062219.22618-1-Kuan-Ying.Lee@mediatek.com
Signed-off-by: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
Reviewed-by: Dmitry Vyukov <dvyukov@google.com>
Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>
Cc: Andrey Ryabinin <ryabinin.a.a@gmail.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Vincenzo Frascino <vincenzo.frascino@arm.com>
Cc: Matthias Brugger <matthias.bgg@gmail.com>
Cc: Chinwen Chang <chinwen.chang@mediatek.com>
Cc: Yee Lee <yee.lee@mediatek.com>
Cc: Andrew Yang <andrew.yang@mediatek.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
mm/kasan/common.c
mm/kasan/kasan.h
mm/kasan/report.c

index c40c0e7b3b5f1da97efbe9b409b1002ce2b83304..707c3a527fcbd55c3236dda3524d5ff84cec25ea 100644 (file)
@@ -343,7 +343,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
 
        if (unlikely(nearest_obj(cache, virt_to_slab(object), object) !=
            object)) {
-               kasan_report_invalid_free(tagged_object, ip);
+               kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_INVALID_FREE);
                return true;
        }
 
@@ -352,7 +352,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
                return false;
 
        if (!kasan_byte_accessible(tagged_object)) {
-               kasan_report_invalid_free(tagged_object, ip);
+               kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_DOUBLE_FREE);
                return true;
        }
 
@@ -377,12 +377,12 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object,
 static inline bool ____kasan_kfree_large(void *ptr, unsigned long ip)
 {
        if (ptr != page_address(virt_to_head_page(ptr))) {
-               kasan_report_invalid_free(ptr, ip);
+               kasan_report_invalid_free(ptr, ip, KASAN_REPORT_INVALID_FREE);
                return true;
        }
 
        if (!kasan_byte_accessible(ptr)) {
-               kasan_report_invalid_free(ptr, ip);
+               kasan_report_invalid_free(ptr, ip, KASAN_REPORT_DOUBLE_FREE);
                return true;
        }
 
index 610d60d6e5b8308b3aff50223a63ca70df1a6d4b..01c03e45acd42afef5469ba3cf2e6d46a1233e06 100644 (file)
@@ -125,6 +125,7 @@ static inline bool kasan_sync_fault_possible(void)
 enum kasan_report_type {
        KASAN_REPORT_ACCESS,
        KASAN_REPORT_INVALID_FREE,
+       KASAN_REPORT_DOUBLE_FREE,
 };
 
 struct kasan_report_info {
@@ -277,7 +278,7 @@ static inline void kasan_print_address_stack_frame(const void *addr) { }
 
 bool kasan_report(unsigned long addr, size_t size,
                bool is_write, unsigned long ip);
-void kasan_report_invalid_free(void *object, unsigned long ip);
+void kasan_report_invalid_free(void *object, unsigned long ip, enum kasan_report_type type);
 
 struct page *kasan_addr_to_page(const void *addr);
 struct slab *kasan_addr_to_slab(const void *addr);
index b341a191651d36df9f11810f47035ef388d7ba51..fe3f606b3a9867af9fc8db923fc638f923a0f4d3 100644 (file)
@@ -176,8 +176,12 @@ static void end_report(unsigned long *flags, void *addr)
 static void print_error_description(struct kasan_report_info *info)
 {
        if (info->type == KASAN_REPORT_INVALID_FREE) {
-               pr_err("BUG: KASAN: double-free or invalid-free in %pS\n",
-                      (void *)info->ip);
+               pr_err("BUG: KASAN: invalid-free in %pS\n", (void *)info->ip);
+               return;
+       }
+
+       if (info->type == KASAN_REPORT_DOUBLE_FREE) {
+               pr_err("BUG: KASAN: double-free in %pS\n", (void *)info->ip);
                return;
        }
 
@@ -433,7 +437,7 @@ static void print_report(struct kasan_report_info *info)
        }
 }
 
-void kasan_report_invalid_free(void *ptr, unsigned long ip)
+void kasan_report_invalid_free(void *ptr, unsigned long ip, enum kasan_report_type type)
 {
        unsigned long flags;
        struct kasan_report_info info;
@@ -448,7 +452,7 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip)
 
        start_report(&flags, true);
 
-       info.type = KASAN_REPORT_INVALID_FREE;
+       info.type = type;
        info.access_addr = ptr;
        info.first_bad_addr = kasan_reset_tag(ptr);
        info.access_size = 0;