11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause 21d171f95SJens Wiklander /* 31d171f95SJens Wiklander * Copyright (c) 2016, Linaro Limited 41d171f95SJens Wiklander */ 51d171f95SJens Wiklander 61d171f95SJens Wiklander #include <assert.h> 71d171f95SJens Wiklander #include <compiler.h> 80b1d6bacSJens Wiklander #include <keep.h> 91d171f95SJens Wiklander #include <kernel/asan.h> 100b1d6bacSJens Wiklander #include <kernel/panic.h> 11c9c847d5SAleksandr Iashchenko #include <printk.h> 12*7749dda2SAleksandr Iashchenko #include <setjmp.h> 131d171f95SJens Wiklander #include <string.h> 140b1d6bacSJens Wiklander #include <trace.h> 151d171f95SJens Wiklander #include <types_ext.h> 161d171f95SJens Wiklander #include <util.h> 171d171f95SJens Wiklander 1857cf66e1SJens Wiklander #if __GCC_VERSION >= 70000 1957cf66e1SJens Wiklander #define ASAN_ABI_VERSION 7 2057cf66e1SJens Wiklander #else 2157cf66e1SJens Wiklander #define ASAN_ABI_VERSION 6 2257cf66e1SJens Wiklander #endif 2357cf66e1SJens Wiklander 241d171f95SJens Wiklander struct asan_source_location { 251d171f95SJens Wiklander const char *file_name; 261d171f95SJens Wiklander int line_no; 271d171f95SJens Wiklander int column_no; 281d171f95SJens Wiklander }; 291d171f95SJens Wiklander 301d171f95SJens Wiklander struct asan_global { 311d171f95SJens Wiklander uintptr_t beg; 321d171f95SJens Wiklander uintptr_t size; 331d171f95SJens Wiklander uintptr_t size_with_redzone; 341d171f95SJens Wiklander const char *name; 351d171f95SJens Wiklander const char *module_name; 361d171f95SJens Wiklander uintptr_t has_dynamic_init; 371d171f95SJens Wiklander struct asan_source_location *location; 3857cf66e1SJens Wiklander #if ASAN_ABI_VERSION >= 7 3957cf66e1SJens Wiklander uintptr_t odr_indicator; 4057cf66e1SJens Wiklander #endif 411d171f95SJens Wiklander }; 421d171f95SJens Wiklander 431d171f95SJens Wiklander static vaddr_t asan_va_base; 441d171f95SJens Wiklander static size_t asan_va_size; 451d171f95SJens Wiklander static bool asan_active; 46c9c847d5SAleksandr Iashchenko static asan_panic_cb_t asan_panic_cb = asan_panic; 471d171f95SJens Wiklander 48bce4951cSJens Wiklander static int8_t *va_to_shadow(const void *va) 491d171f95SJens Wiklander { 501d171f95SJens Wiklander vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET; 511d171f95SJens Wiklander 521d171f95SJens Wiklander return (int8_t *)sa; 531d171f95SJens Wiklander } 541d171f95SJens Wiklander 55bce4951cSJens Wiklander static size_t va_range_to_shadow_size(const void *begin, const void *end) 561d171f95SJens Wiklander { 571d171f95SJens Wiklander return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE; 581d171f95SJens Wiklander } 591d171f95SJens Wiklander 60bce4951cSJens Wiklander static bool va_range_inside_shadow(const void *begin, const void *end) 611d171f95SJens Wiklander { 621d171f95SJens Wiklander vaddr_t b = (vaddr_t)begin; 631d171f95SJens Wiklander vaddr_t e = (vaddr_t)end; 641d171f95SJens Wiklander 651d171f95SJens Wiklander if (b >= e) 661d171f95SJens Wiklander return false; 671d171f95SJens Wiklander return (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size)); 681d171f95SJens Wiklander } 691d171f95SJens Wiklander 70bce4951cSJens Wiklander static bool va_range_outside_shadow(const void *begin, const void *end) 711d171f95SJens Wiklander { 721d171f95SJens Wiklander vaddr_t b = (vaddr_t)begin; 731d171f95SJens Wiklander vaddr_t e = (vaddr_t)end; 741d171f95SJens Wiklander 751d171f95SJens Wiklander if (b >= e) 761d171f95SJens Wiklander return false; 771d171f95SJens Wiklander return (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size)); 781d171f95SJens Wiklander } 791d171f95SJens Wiklander 80bce4951cSJens Wiklander static size_t va_misalignment(const void *va) 811d171f95SJens Wiklander { 821d171f95SJens Wiklander return (vaddr_t)va & ASAN_BLOCK_MASK; 831d171f95SJens Wiklander } 841d171f95SJens Wiklander 85bce4951cSJens Wiklander static bool va_is_well_aligned(const void *va) 861d171f95SJens Wiklander { 871d171f95SJens Wiklander return !va_misalignment(va); 881d171f95SJens Wiklander } 891d171f95SJens Wiklander 90bce4951cSJens Wiklander void asan_set_shadowed(const void *begin, const void *end) 911d171f95SJens Wiklander { 921d171f95SJens Wiklander vaddr_t b = (vaddr_t)begin; 931d171f95SJens Wiklander vaddr_t e = (vaddr_t)end; 941d171f95SJens Wiklander 951d171f95SJens Wiklander assert(!asan_va_base); 961d171f95SJens Wiklander assert(va_is_well_aligned(begin)); 971d171f95SJens Wiklander assert(va_is_well_aligned(end)); 981d171f95SJens Wiklander assert(b < e); 991d171f95SJens Wiklander 1001d171f95SJens Wiklander asan_va_base = b; 1011d171f95SJens Wiklander asan_va_size = e - b; 1021d171f95SJens Wiklander } 1031d171f95SJens Wiklander 104bce4951cSJens Wiklander void asan_tag_no_access(const void *begin, const void *end) 1051d171f95SJens Wiklander { 1061d171f95SJens Wiklander assert(va_is_well_aligned(begin)); 1071d171f95SJens Wiklander assert(va_is_well_aligned(end)); 1081d171f95SJens Wiklander assert(va_range_inside_shadow(begin, end)); 1091d171f95SJens Wiklander 110abccd909SJens Wiklander asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE, 1111d171f95SJens Wiklander va_range_to_shadow_size(begin, end)); 1121d171f95SJens Wiklander } 1131d171f95SJens Wiklander 114bce4951cSJens Wiklander void asan_tag_access(const void *begin, const void *end) 1151d171f95SJens Wiklander { 11624fe8015SJens Wiklander if (!asan_va_base || (begin == end)) 1171d171f95SJens Wiklander return; 1181d171f95SJens Wiklander 1191d171f95SJens Wiklander assert(va_range_inside_shadow(begin, end)); 1201d171f95SJens Wiklander assert(va_is_well_aligned(begin)); 1211d171f95SJens Wiklander 122abccd909SJens Wiklander asan_memset_unchecked(va_to_shadow(begin), 0, 123abccd909SJens Wiklander va_range_to_shadow_size(begin, end)); 1241d171f95SJens Wiklander if (!va_is_well_aligned(end)) 1251d171f95SJens Wiklander *va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end); 1261d171f95SJens Wiklander } 1271d171f95SJens Wiklander 128bce4951cSJens Wiklander void asan_tag_heap_free(const void *begin, const void *end) 1291d171f95SJens Wiklander { 1301d171f95SJens Wiklander if (!asan_va_base) 1311d171f95SJens Wiklander return; 1321d171f95SJens Wiklander 1331d171f95SJens Wiklander assert(va_range_inside_shadow(begin, end)); 1341d171f95SJens Wiklander assert(va_is_well_aligned(begin)); 1351d171f95SJens Wiklander assert(va_is_well_aligned(end)); 1361d171f95SJens Wiklander 137abccd909SJens Wiklander asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE, 1381d171f95SJens Wiklander va_range_to_shadow_size(begin, end)); 1391d171f95SJens Wiklander } 1401d171f95SJens Wiklander 1415bea6aedSJerome Forissier __inhibit_loop_to_libcall void *asan_memset_unchecked(void *s, int c, size_t n) 142abccd909SJens Wiklander { 143abccd909SJens Wiklander uint8_t *b = s; 144abccd909SJens Wiklander size_t m; 145abccd909SJens Wiklander 146abccd909SJens Wiklander for (m = 0; m < n; m++) 147abccd909SJens Wiklander b[m] = c; 148abccd909SJens Wiklander 149abccd909SJens Wiklander return s; 150abccd909SJens Wiklander } 151abccd909SJens Wiklander 1525bea6aedSJerome Forissier __inhibit_loop_to_libcall 15306fe4216SJens Wiklander void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src, 15406fe4216SJens Wiklander size_t len) 15506fe4216SJens Wiklander { 15606fe4216SJens Wiklander uint8_t *__restrict d = dst; 15706fe4216SJens Wiklander const uint8_t *__restrict s = src; 15806fe4216SJens Wiklander size_t n; 15906fe4216SJens Wiklander 16006fe4216SJens Wiklander for (n = 0; n < len; n++) 16106fe4216SJens Wiklander d[n] = s[n]; 16206fe4216SJens Wiklander 16306fe4216SJens Wiklander return dst; 16406fe4216SJens Wiklander } 16506fe4216SJens Wiklander 1661d171f95SJens Wiklander void asan_start(void) 1671d171f95SJens Wiklander { 1681d171f95SJens Wiklander assert(asan_va_base && !asan_active); 1691d171f95SJens Wiklander asan_active = true; 1701d171f95SJens Wiklander } 1711d171f95SJens Wiklander 172c9c847d5SAleksandr Iashchenko void __noreturn asan_panic(void) 173c9c847d5SAleksandr Iashchenko { 174c9c847d5SAleksandr Iashchenko panic(); 175c9c847d5SAleksandr Iashchenko } 176c9c847d5SAleksandr Iashchenko 177c9c847d5SAleksandr Iashchenko void asan_set_panic_cb(asan_panic_cb_t panic_cb) 178c9c847d5SAleksandr Iashchenko { 179c9c847d5SAleksandr Iashchenko asan_panic_cb = panic_cb; 180c9c847d5SAleksandr Iashchenko } 181c9c847d5SAleksandr Iashchenko 182c9c847d5SAleksandr Iashchenko static void asan_report(vaddr_t addr, size_t size) 183c9c847d5SAleksandr Iashchenko { 184c9c847d5SAleksandr Iashchenko #ifdef KASAN_DUMP_SHADOW 185c9c847d5SAleksandr Iashchenko char buf[128] = {0}; 186c9c847d5SAleksandr Iashchenko int r = 0, rc = 0; 187c9c847d5SAleksandr Iashchenko vaddr_t b = 0, e = 0, saddr = 0; 188c9c847d5SAleksandr Iashchenko 189c9c847d5SAleksandr Iashchenko b = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) - ASAN_BLOCK_SIZE; 190c9c847d5SAleksandr Iashchenko e = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) + ASAN_BLOCK_SIZE; 191c9c847d5SAleksandr Iashchenko 192c9c847d5SAleksandr Iashchenko /* Print shadow map nearby */ 193c9c847d5SAleksandr Iashchenko if (va_range_inside_shadow((void *)b, (void *)e)) { 194c9c847d5SAleksandr Iashchenko rc = snprintk(buf + r, sizeof(buf) - r, "%lx: ", b); 195c9c847d5SAleksandr Iashchenko assert(rc > 0); 196c9c847d5SAleksandr Iashchenko r += rc; 197c9c847d5SAleksandr Iashchenko for (saddr = b; saddr <= e; saddr += ASAN_BLOCK_SIZE) { 198c9c847d5SAleksandr Iashchenko int8_t *sbyte = va_to_shadow((void *)saddr); 199c9c847d5SAleksandr Iashchenko 200c9c847d5SAleksandr Iashchenko rc = snprintk(buf + r, sizeof(buf) - r, 201c9c847d5SAleksandr Iashchenko "0x%02x ", (uint8_t)*sbyte); 202c9c847d5SAleksandr Iashchenko assert(rc > 0); 203c9c847d5SAleksandr Iashchenko r += rc; 204c9c847d5SAleksandr Iashchenko } 205c9c847d5SAleksandr Iashchenko EMSG("%s", buf); 206c9c847d5SAleksandr Iashchenko } 207c9c847d5SAleksandr Iashchenko #endif 208c9c847d5SAleksandr Iashchenko EMSG("[ASAN]: access violation, addr: %lx size: %zu\n", 209c9c847d5SAleksandr Iashchenko addr, size); 210c9c847d5SAleksandr Iashchenko 211c9c847d5SAleksandr Iashchenko asan_panic_cb(); 212c9c847d5SAleksandr Iashchenko } 213c9c847d5SAleksandr Iashchenko 2141d171f95SJens Wiklander static void check_access(vaddr_t addr, size_t size) 2151d171f95SJens Wiklander { 2161d171f95SJens Wiklander void *begin = (void *)addr; 2171d171f95SJens Wiklander void *end = (void *)(addr + size); 2181d171f95SJens Wiklander int8_t *a; 2191d171f95SJens Wiklander int8_t *e; 2201d171f95SJens Wiklander 2211d171f95SJens Wiklander if (!asan_active || !size) 2221d171f95SJens Wiklander return; 2231d171f95SJens Wiklander if (va_range_outside_shadow(begin, end)) 2241d171f95SJens Wiklander return; 2251d171f95SJens Wiklander /* 2261d171f95SJens Wiklander * If it isn't outside it has to be completely inside or there's a 2271d171f95SJens Wiklander * problem. 2281d171f95SJens Wiklander */ 2291d171f95SJens Wiklander if (!va_range_inside_shadow(begin, end)) 2301d171f95SJens Wiklander panic(); 2311d171f95SJens Wiklander 232726ce13eSJens Wiklander e = va_to_shadow((void *)(addr + size - 1)); 233726ce13eSJens Wiklander for (a = va_to_shadow(begin); a <= e; a++) 2341d171f95SJens Wiklander if (*a < 0) 235c9c847d5SAleksandr Iashchenko asan_report(addr, size); 2361d171f95SJens Wiklander 2371d171f95SJens Wiklander if (!va_is_well_aligned(end) && 2381d171f95SJens Wiklander va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE)) 239c9c847d5SAleksandr Iashchenko asan_report(addr, size); 2401d171f95SJens Wiklander } 2411d171f95SJens Wiklander 2421d171f95SJens Wiklander static void check_load(vaddr_t addr, size_t size) 2431d171f95SJens Wiklander { 2441d171f95SJens Wiklander check_access(addr, size); 2451d171f95SJens Wiklander } 2461d171f95SJens Wiklander 2471d171f95SJens Wiklander static void check_store(vaddr_t addr, size_t size) 2481d171f95SJens Wiklander { 2491d171f95SJens Wiklander check_access(addr, size); 2501d171f95SJens Wiklander } 2511d171f95SJens Wiklander 2521d171f95SJens Wiklander static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused) 2531d171f95SJens Wiklander { 2541d171f95SJens Wiklander panic(); 2551d171f95SJens Wiklander } 2561d171f95SJens Wiklander 2571d171f95SJens Wiklander static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused) 2581d171f95SJens Wiklander { 2591d171f95SJens Wiklander panic(); 2601d171f95SJens Wiklander } 2611d171f95SJens Wiklander 2621d171f95SJens Wiklander 2631d171f95SJens Wiklander 2641d171f95SJens Wiklander #define DEFINE_ASAN_FUNC(type, size) \ 2651d171f95SJens Wiklander void __asan_##type##size(vaddr_t addr); \ 2661d171f95SJens Wiklander void __asan_##type##size(vaddr_t addr) \ 2671d171f95SJens Wiklander { check_##type(addr, size); } \ 2681d171f95SJens Wiklander void __asan_##type##size##_noabort(vaddr_t addr); \ 2691d171f95SJens Wiklander void __asan_##type##size##_noabort(vaddr_t addr) \ 2701d171f95SJens Wiklander { check_##type(addr, size); } \ 2711d171f95SJens Wiklander void __asan_report_##type##size##_noabort(vaddr_t addr);\ 2721d171f95SJens Wiklander void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \ 2731d171f95SJens Wiklander { report_##type(addr, size); } 2741d171f95SJens Wiklander 2751d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 1) 2761d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 2) 2771d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 4) 2781d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 8) 2791d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 16) 2801d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 1) 2811d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 2) 2821d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 4) 2831d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 8) 2841d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 16) 2851d171f95SJens Wiklander 2861d171f95SJens Wiklander void __asan_loadN_noabort(vaddr_t addr, size_t size); 2871d171f95SJens Wiklander void __asan_loadN_noabort(vaddr_t addr, size_t size) 2881d171f95SJens Wiklander { 2891d171f95SJens Wiklander check_load(addr, size); 2901d171f95SJens Wiklander } 2911d171f95SJens Wiklander 2921d171f95SJens Wiklander void __asan_storeN_noabort(vaddr_t addr, size_t size); 2931d171f95SJens Wiklander void __asan_storeN_noabort(vaddr_t addr, size_t size) 2941d171f95SJens Wiklander { 2951d171f95SJens Wiklander check_store(addr, size); 2961d171f95SJens Wiklander } 2971d171f95SJens Wiklander 2981d171f95SJens Wiklander void __asan_report_load_n_noabort(vaddr_t addr, size_t size); 2991d171f95SJens Wiklander void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size) 3001d171f95SJens Wiklander { 3011d171f95SJens Wiklander report_load(addr, size); 3021d171f95SJens Wiklander } 3031d171f95SJens Wiklander 3041d171f95SJens Wiklander void __asan_report_store_n_noabort(vaddr_t addr, size_t size); 3051d171f95SJens Wiklander void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size) 3061d171f95SJens Wiklander { 3071d171f95SJens Wiklander report_store(addr, size); 3081d171f95SJens Wiklander } 3091d171f95SJens Wiklander 3101d171f95SJens Wiklander void __asan_handle_no_return(void); 311ce553c81SJens Wiklander void __asan_handle_no_return(void) 3121d171f95SJens Wiklander { 3131d171f95SJens Wiklander } 3141d171f95SJens Wiklander 3151d171f95SJens Wiklander void __asan_register_globals(struct asan_global *globals, size_t size); 3161d171f95SJens Wiklander void __asan_register_globals(struct asan_global *globals, size_t size) 3171d171f95SJens Wiklander { 3181d171f95SJens Wiklander size_t n; 3191d171f95SJens Wiklander 3201d171f95SJens Wiklander for (n = 0; n < size; n++) 3211d171f95SJens Wiklander asan_tag_access((void *)globals[n].beg, 3221d171f95SJens Wiklander (void *)(globals[n].beg + globals[n].size)); 3231d171f95SJens Wiklander } 3243639b55fSJerome Forissier DECLARE_KEEP_INIT(__asan_register_globals); 3251d171f95SJens Wiklander 3261d171f95SJens Wiklander void __asan_unregister_globals(struct asan_global *globals, size_t size); 3271d171f95SJens Wiklander void __asan_unregister_globals(struct asan_global *globals __unused, 3281d171f95SJens Wiklander size_t size __unused) 3291d171f95SJens Wiklander { 3301d171f95SJens Wiklander } 331*7749dda2SAleksandr Iashchenko 332*7749dda2SAleksandr Iashchenko void asan_handle_longjmp(void *old_sp) 333*7749dda2SAleksandr Iashchenko { 334*7749dda2SAleksandr Iashchenko void *top = old_sp; 335*7749dda2SAleksandr Iashchenko void *bottom = (void *)ROUNDDOWN((vaddr_t)&top, 336*7749dda2SAleksandr Iashchenko ASAN_BLOCK_SIZE); 337*7749dda2SAleksandr Iashchenko 338*7749dda2SAleksandr Iashchenko asan_tag_access(bottom, top); 339*7749dda2SAleksandr Iashchenko } 340