Commit 3de0de75 authored by Kuan-Ying Lee's avatar Kuan-Ying Lee Committed by akpm

kasan: separate double free case from invalid free

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.comSigned-off-by: default avatarKuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
Reviewed-by: default avatarDmitry Vyukov <dvyukov@google.com>
Reviewed-by: default avatarAndrey 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: default avatarAndrew Morton <akpm@linux-foundation.org>
parent cb55b838
...@@ -343,7 +343,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object, ...@@ -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) != if (unlikely(nearest_obj(cache, virt_to_slab(object), object) !=
object)) { object)) {
kasan_report_invalid_free(tagged_object, ip); kasan_report_invalid_free(tagged_object, ip, KASAN_REPORT_INVALID_FREE);
return true; return true;
} }
...@@ -352,7 +352,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object, ...@@ -352,7 +352,7 @@ static inline bool ____kasan_slab_free(struct kmem_cache *cache, void *object,
return false; return false;
if (!kasan_byte_accessible(tagged_object)) { 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; return true;
} }
...@@ -377,12 +377,12 @@ bool __kasan_slab_free(struct kmem_cache *cache, void *object, ...@@ -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) static inline bool ____kasan_kfree_large(void *ptr, unsigned long ip)
{ {
if (ptr != page_address(virt_to_head_page(ptr))) { 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; return true;
} }
if (!kasan_byte_accessible(ptr)) { if (!kasan_byte_accessible(ptr)) {
kasan_report_invalid_free(ptr, ip); kasan_report_invalid_free(ptr, ip, KASAN_REPORT_DOUBLE_FREE);
return true; return true;
} }
......
...@@ -125,6 +125,7 @@ static inline bool kasan_sync_fault_possible(void) ...@@ -125,6 +125,7 @@ static inline bool kasan_sync_fault_possible(void)
enum kasan_report_type { enum kasan_report_type {
KASAN_REPORT_ACCESS, KASAN_REPORT_ACCESS,
KASAN_REPORT_INVALID_FREE, KASAN_REPORT_INVALID_FREE,
KASAN_REPORT_DOUBLE_FREE,
}; };
struct kasan_report_info { struct kasan_report_info {
...@@ -277,7 +278,7 @@ static inline void kasan_print_address_stack_frame(const void *addr) { } ...@@ -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 kasan_report(unsigned long addr, size_t size,
bool is_write, unsigned long ip); 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 page *kasan_addr_to_page(const void *addr);
struct slab *kasan_addr_to_slab(const void *addr); struct slab *kasan_addr_to_slab(const void *addr);
......
...@@ -176,8 +176,12 @@ static void end_report(unsigned long *flags, void *addr) ...@@ -176,8 +176,12 @@ static void end_report(unsigned long *flags, void *addr)
static void print_error_description(struct kasan_report_info *info) static void print_error_description(struct kasan_report_info *info)
{ {
if (info->type == KASAN_REPORT_INVALID_FREE) { if (info->type == KASAN_REPORT_INVALID_FREE) {
pr_err("BUG: KASAN: double-free or invalid-free in %pS\n", pr_err("BUG: KASAN: invalid-free in %pS\n", (void *)info->ip);
(void *)info->ip); return;
}
if (info->type == KASAN_REPORT_DOUBLE_FREE) {
pr_err("BUG: KASAN: double-free in %pS\n", (void *)info->ip);
return; return;
} }
...@@ -433,7 +437,7 @@ static void print_report(struct kasan_report_info *info) ...@@ -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; unsigned long flags;
struct kasan_report_info info; struct kasan_report_info info;
...@@ -448,7 +452,7 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip) ...@@ -448,7 +452,7 @@ void kasan_report_invalid_free(void *ptr, unsigned long ip)
start_report(&flags, true); start_report(&flags, true);
info.type = KASAN_REPORT_INVALID_FREE; info.type = type;
info.access_addr = ptr; info.access_addr = ptr;
info.first_bad_addr = kasan_reset_tag(ptr); info.first_bad_addr = kasan_reset_tag(ptr);
info.access_size = 0; info.access_size = 0;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment