blob: ecede06ef374aafc7cf0d5f275667ee0499a956a [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2014 Samsung Electronics Co., Ltd.
* Copyright (c) 2020 Google, Inc.
*/
#include <linux/atomic.h>
#include "kasan.h"
extern struct kasan_stack_ring stack_ring;
static const char *get_common_bug_type(struct kasan_report_info *info)
{
/*
* If access_size is a negative number, then it has reason to be
* defined as out-of-bounds bug type.
*
* Casting negative numbers to size_t would indeed turn up as
* a large size_t and its value will be larger than ULONG_MAX/2,
* so that this can qualify as out-of-bounds.
*/
if (info->access_addr + info->access_size < info->access_addr)
return "out-of-bounds";
return "invalid-access";
}
void kasan_complete_mode_report_info(struct kasan_report_info *info)
{
unsigned long flags;
u64 pos;
struct kasan_stack_ring_entry *entry;
void *ptr;
u32 pid;
depot_stack_handle_t stack;
bool is_free;
bool alloc_found = false, free_found = false;
if ((!info->cache || !info->object) && !info->bug_type) {
info->bug_type = get_common_bug_type(info);
return;
}
write_lock_irqsave(&stack_ring.lock, flags);
pos = atomic64_read(&stack_ring.pos);
/*
* The loop below tries to find stack ring entries relevant to the
* buggy object. This is a best-effort process.
*
* First, another object with the same tag can be allocated in place of
* the buggy object. Also, since the number of entries is limited, the
* entries relevant to the buggy object can be overwritten.
*/
for (u64 i = pos - 1; i != pos - 1 - stack_ring.size; i--) {
if (alloc_found && free_found)
break;
entry = &stack_ring.entries[i % stack_ring.size];
/* Paired with smp_store_release() in save_stack_info(). */
ptr = (void *)smp_load_acquire(&entry->ptr);
if (kasan_reset_tag(ptr) != info->object ||
get_tag(ptr) != get_tag(info->access_addr))
continue;
pid = READ_ONCE(entry->pid);
stack = READ_ONCE(entry->stack);
is_free = READ_ONCE(entry->is_free);
if (is_free) {
/*
* Second free of the same object.
* Give up on trying to find the alloc entry.
*/
if (free_found)
break;
info->free_track.pid = pid;
info->free_track.stack = stack;
free_found = true;
/*
* If a free entry is found first, the bug is likely
* a use-after-free.
*/
if (!info->bug_type)
info->bug_type = "use-after-free";
} else {
/* Second alloc of the same object. Give up. */
if (alloc_found)
break;
info->alloc_track.pid = pid;
info->alloc_track.stack = stack;
alloc_found = true;
/*
* If an alloc entry is found first, the bug is likely
* an out-of-bounds.
*/
if (!info->bug_type)
info->bug_type = "slab-out-of-bounds";
}
}
write_unlock_irqrestore(&stack_ring.lock, flags);
/* Assign the common bug type if no entries were found. */
if (!info->bug_type)
info->bug_type = get_common_bug_type(info);
}