// 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 = "slab-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); }