diff mbox series

kasan: separate double free case from invalid free

Message ID 20220615062219.22618-1-Kuan-Ying.Lee@mediatek.com (mailing list archive)
State New
Headers show
Series kasan: separate double free case from invalid free | expand

Commit Message

Kuan-Ying Lee (李冠穎) June 15, 2022, 6:22 a.m. UTC
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

Signed-off-by: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
---
 mm/kasan/common.c |  8 ++++----
 mm/kasan/kasan.h  |  3 ++-
 mm/kasan/report.c | 12 ++++++++----
 3 files changed, 14 insertions(+), 9 deletions(-)

Comments

Andrew Morton July 3, 2022, 11:15 p.m. UTC | #1
On Wed, 15 Jun 2022 14:22:18 +0800 Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com> wrote:

> 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
> 
> ...

Could we please have some review of this?

Thanks.

> 
> diff --git a/mm/kasan/common.c b/mm/kasan/common.c
> index c40c0e7b3b5f..707c3a527fcb 100644
> --- a/mm/kasan/common.c
> +++ b/mm/kasan/common.c
> @@ -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;
>  	}
>  
> diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> index 610d60d6e5b8..01c03e45acd4 100644
> --- a/mm/kasan/kasan.h
> +++ b/mm/kasan/kasan.h
> @@ -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);
> diff --git a/mm/kasan/report.c b/mm/kasan/report.c
> index b341a191651d..fe3f606b3a98 100644
> --- a/mm/kasan/report.c
> +++ b/mm/kasan/report.c
> @@ -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;
> -- 
> 2.18.0
>
Dmitry Vyukov July 4, 2022, 7:46 a.m. UTC | #2
On Mon, 4 Jul 2022 at 01:15, Andrew Morton <akpm@linux-foundation.org> wrote:
>
> On Wed, 15 Jun 2022 14:22:18 +0800 Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com> wrote:
>
> > 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
> >
> > ...
>
> Could we please have some review of this?


Looks reasonable to me.
Looking through git log it seems the only reason to combine them was
laziness/didn't seem important enough.

Reviewed-by: Dmitry Vyukov <dvyukov@google.com>

I will update syzkaller parsing of bug messages to not produce
duplicates for existing double-frees.
Not sure if anything needs to be done for other kernel testing systems.


> > diff --git a/mm/kasan/common.c b/mm/kasan/common.c
> > index c40c0e7b3b5f..707c3a527fcb 100644
> > --- a/mm/kasan/common.c
> > +++ b/mm/kasan/common.c
> > @@ -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;
> >       }
> >
> > diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> > index 610d60d6e5b8..01c03e45acd4 100644
> > --- a/mm/kasan/kasan.h
> > +++ b/mm/kasan/kasan.h
> > @@ -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);
> > diff --git a/mm/kasan/report.c b/mm/kasan/report.c
> > index b341a191651d..fe3f606b3a98 100644
> > --- a/mm/kasan/report.c
> > +++ b/mm/kasan/report.c
> > @@ -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;
> > --
> > 2.18.0
> >
Andrey Konovalov July 12, 2022, 8:36 p.m. UTC | #3
On Wed, Jun 15, 2022 at 8:22 AM 'Kuan-Ying Lee' via kasan-dev
<kasan-dev@googlegroups.com> wrote:
>
> 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
>
> Signed-off-by: Kuan-Ying Lee <Kuan-Ying.Lee@mediatek.com>
> ---
>  mm/kasan/common.c |  8 ++++----
>  mm/kasan/kasan.h  |  3 ++-
>  mm/kasan/report.c | 12 ++++++++----
>  3 files changed, 14 insertions(+), 9 deletions(-)
>
> diff --git a/mm/kasan/common.c b/mm/kasan/common.c
> index c40c0e7b3b5f..707c3a527fcb 100644
> --- a/mm/kasan/common.c
> +++ b/mm/kasan/common.c
> @@ -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;
>         }
>
> diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
> index 610d60d6e5b8..01c03e45acd4 100644
> --- a/mm/kasan/kasan.h
> +++ b/mm/kasan/kasan.h
> @@ -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);
> diff --git a/mm/kasan/report.c b/mm/kasan/report.c
> index b341a191651d..fe3f606b3a98 100644
> --- a/mm/kasan/report.c
> +++ b/mm/kasan/report.c
> @@ -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;
> --
> 2.18.0

Reviewed-by: Andrey Konovalov <andreyknvl@gmail.com>

Thanks for the patch!
diff mbox series

Patch

diff --git a/mm/kasan/common.c b/mm/kasan/common.c
index c40c0e7b3b5f..707c3a527fcb 100644
--- a/mm/kasan/common.c
+++ b/mm/kasan/common.c
@@ -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;
 	}
 
diff --git a/mm/kasan/kasan.h b/mm/kasan/kasan.h
index 610d60d6e5b8..01c03e45acd4 100644
--- a/mm/kasan/kasan.h
+++ b/mm/kasan/kasan.h
@@ -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);
diff --git a/mm/kasan/report.c b/mm/kasan/report.c
index b341a191651d..fe3f606b3a98 100644
--- a/mm/kasan/report.c
+++ b/mm/kasan/report.c
@@ -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;