// SPDX-License-Identifier: GPL-2.0 /* * This file contains generic KASAN specific error reporting code. * * Copyright (c) 2014 Samsung Electronics Co., Ltd. * Author: Andrey Ryabinin * * Some code borrowed from https://github.com/xairy/kasan-prototype by * Andrey Konovalov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * */ #include #include #include #include "kasan.h" void *find_first_bad_addr(void *addr, size_t size) { void *p = addr; while (p < addr + size && !(*(u8 *)kasan_mem_to_shadow(p))) p += KASAN_SHADOW_SCALE_SIZE; return p; } static const char *get_shadow_bug_type(struct kasan_access_info *info) { const char *bug_type = "unknown-crash"; u8 *shadow_addr; shadow_addr = (u8 *)kasan_mem_to_shadow(info->first_bad_addr); /* * If shadow byte value is in [0, KASAN_SHADOW_SCALE_SIZE) we can look * at the next shadow byte to determine the type of the bad access. */ if (*shadow_addr > 0 && *shadow_addr <= KASAN_SHADOW_SCALE_SIZE - 1) shadow_addr++; switch (*shadow_addr) { case 0 ... KASAN_SHADOW_SCALE_SIZE - 1: /* * In theory it's still possible to see these shadow values * due to a data race in the kernel code. */ bug_type = "out-of-bounds"; break; case KASAN_PAGE_REDZONE: case KASAN_KMALLOC_REDZONE: bug_type = "slab-out-of-bounds"; break; case KASAN_GLOBAL_REDZONE: bug_type = "global-out-of-bounds"; break; case KASAN_STACK_LEFT: case KASAN_STACK_MID: case KASAN_STACK_RIGHT: case KASAN_STACK_PARTIAL: bug_type = "stack-out-of-bounds"; break; case KASAN_FREE_PAGE: case KASAN_KMALLOC_FREE: case KASAN_KMALLOC_FREETRACK: bug_type = "use-after-free"; break; case KASAN_ALLOCA_LEFT: case KASAN_ALLOCA_RIGHT: bug_type = "alloca-out-of-bounds"; break; case KASAN_VMALLOC_INVALID: bug_type = "vmalloc-out-of-bounds"; break; } return bug_type; } static const char *get_wild_bug_type(struct kasan_access_info *info) { const char *bug_type = "unknown-crash"; if ((unsigned long)info->access_addr < PAGE_SIZE) bug_type = "null-ptr-deref"; else bug_type = "wild-memory-access"; return bug_type; } const char *get_bug_type(struct kasan_access_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"; if (addr_has_shadow(info->access_addr)) return get_shadow_bug_type(info); return get_wild_bug_type(info); } #define DEFINE_ASAN_REPORT_LOAD(size) \ void __asan_report_load##size##_noabort(unsigned long addr) \ { \ kasan_report(addr, size, false, _RET_IP_); \ } \ EXPORT_SYMBOL(__asan_report_load##size##_noabort) #define DEFINE_ASAN_REPORT_STORE(size) \ void __asan_report_store##size##_noabort(unsigned long addr) \ { \ kasan_report(addr, size, true, _RET_IP_); \ } \ EXPORT_SYMBOL(__asan_report_store##size##_noabort) DEFINE_ASAN_REPORT_LOAD(1); DEFINE_ASAN_REPORT_LOAD(2); DEFINE_ASAN_REPORT_LOAD(4); DEFINE_ASAN_REPORT_LOAD(8); DEFINE_ASAN_REPORT_LOAD(16); DEFINE_ASAN_REPORT_STORE(1); DEFINE_ASAN_REPORT_STORE(2); DEFINE_ASAN_REPORT_STORE(4); DEFINE_ASAN_REPORT_STORE(8); DEFINE_ASAN_REPORT_STORE(16); void __asan_report_load_n_noabort(unsigned long addr, size_t size) { kasan_report(addr, size, false, _RET_IP_); } EXPORT_SYMBOL(__asan_report_load_n_noabort); void __asan_report_store_n_noabort(unsigned long addr, size_t size) { kasan_report(addr, size, true, _RET_IP_); } EXPORT_SYMBOL(__asan_report_store_n_noabort);