Skip to content

Commit

Permalink
kasan: improve double-free reports
Browse files Browse the repository at this point in the history
Currently we just dump stack in case of double free bug.
Let's dump all info about the object that we have.

[aryabinin@virtuozzo.com: change double free message per Alexander]
  Link: http://lkml.kernel.org/r/1470153654-30160-1-git-send-email-aryabinin@virtuozzo.com
Link: http://lkml.kernel.org/r/1470062715-14077-6-git-send-email-aryabinin@virtuozzo.com
Signed-off-by: Andrey Ryabinin <aryabinin@virtuozzo.com>
Cc: Alexander Potapenko <glider@google.com>
Cc: Dmitry Vyukov <dvyukov@google.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Andrey Ryabinin authored and Linus Torvalds committed Aug 2, 2016
1 parent b3cbd9b commit 7e08897
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 18 deletions.
3 changes: 1 addition & 2 deletions mm/kasan/kasan.c
Original file line number Diff line number Diff line change
Expand Up @@ -543,8 +543,7 @@ bool kasan_slab_free(struct kmem_cache *cache, void *object)

shadow_byte = READ_ONCE(*(s8 *)kasan_mem_to_shadow(object));
if (shadow_byte < 0 || shadow_byte >= KASAN_SHADOW_SCALE_SIZE) {
pr_err("Double free");
dump_stack();
kasan_report_double_free(cache, object, shadow_byte);
return true;
}

Expand Down
2 changes: 2 additions & 0 deletions mm/kasan/kasan.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ static inline bool kasan_report_enabled(void)

void kasan_report(unsigned long addr, size_t size,
bool is_write, unsigned long ip);
void kasan_report_double_free(struct kmem_cache *cache, void *object,
s8 shadow);

#if defined(CONFIG_SLAB) || defined(CONFIG_SLUB)
void quarantine_put(struct kasan_free_meta *info, struct kmem_cache *cache);
Expand Down
54 changes: 38 additions & 16 deletions mm/kasan/report.c
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ static inline bool init_task_stack_addr(const void *addr)
sizeof(init_thread_union.stack));
}

static DEFINE_SPINLOCK(report_lock);

static void kasan_start_report(unsigned long *flags)
{
/*
* Make sure we don't end up in loop.
*/
kasan_disable_current();
spin_lock_irqsave(&report_lock, *flags);
pr_err("==================================================================\n");
}

static void kasan_end_report(unsigned long *flags)
{
pr_err("==================================================================\n");
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irqrestore(&report_lock, *flags);
kasan_enable_current();
}

static void print_track(struct kasan_track *track)
{
pr_err("PID = %u\n", track->pid);
Expand All @@ -129,8 +149,7 @@ static void print_track(struct kasan_track *track)
}
}

static void kasan_object_err(struct kmem_cache *cache, struct page *page,
void *object, char *unused_reason)
static void kasan_object_err(struct kmem_cache *cache, void *object)
{
struct kasan_alloc_meta *alloc_info = get_alloc_info(cache, object);

Expand All @@ -147,6 +166,18 @@ static void kasan_object_err(struct kmem_cache *cache, struct page *page,
print_track(&alloc_info->free_track);
}

void kasan_report_double_free(struct kmem_cache *cache, void *object,
s8 shadow)
{
unsigned long flags;

kasan_start_report(&flags);
pr_err("BUG: Double free or freeing an invalid pointer\n");
pr_err("Unexpected shadow byte: 0x%hhX\n", shadow);
kasan_object_err(cache, object);
kasan_end_report(&flags);
}

static void print_address_description(struct kasan_access_info *info)
{
const void *addr = info->access_addr;
Expand All @@ -160,8 +191,7 @@ static void print_address_description(struct kasan_access_info *info)
struct kmem_cache *cache = page->slab_cache;
object = nearest_obj(cache, page,
(void *)info->access_addr);
kasan_object_err(cache, page, object,
"kasan: bad access detected");
kasan_object_err(cache, object);
return;
}
dump_page(page, "kasan: bad access detected");
Expand Down Expand Up @@ -226,19 +256,13 @@ static void print_shadow_for_address(const void *addr)
}
}

static DEFINE_SPINLOCK(report_lock);

static void kasan_report_error(struct kasan_access_info *info)
{
unsigned long flags;
const char *bug_type;

/*
* Make sure we don't end up in loop.
*/
kasan_disable_current();
spin_lock_irqsave(&report_lock, flags);
pr_err("==================================================================\n");
kasan_start_report(&flags);

if (info->access_addr <
kasan_shadow_to_mem((void *)KASAN_SHADOW_START)) {
if ((unsigned long)info->access_addr < PAGE_SIZE)
Expand All @@ -259,10 +283,8 @@ static void kasan_report_error(struct kasan_access_info *info)
print_address_description(info);
print_shadow_for_address(info->first_bad_addr);
}
pr_err("==================================================================\n");
add_taint(TAINT_BAD_PAGE, LOCKDEP_NOW_UNRELIABLE);
spin_unlock_irqrestore(&report_lock, flags);
kasan_enable_current();

kasan_end_report(&flags);
}

void kasan_report(unsigned long addr, size_t size,
Expand Down

0 comments on commit 7e08897

Please sign in to comment.