1d6d1731bSAleksandr Iashchenko // SPDX-License-Identifier: BSD-2-Clause 2d6d1731bSAleksandr Iashchenko /* 3d6d1731bSAleksandr Iashchenko * Copyright (c) 2016, Linaro Limited 4d6d1731bSAleksandr Iashchenko * Copyright (c) 2018-2020 Maxime Villard, m00nbsd.net 5d6d1731bSAleksandr Iashchenko */ 6d6d1731bSAleksandr Iashchenko 7d6d1731bSAleksandr Iashchenko #include <asan.h> 8d6d1731bSAleksandr Iashchenko #include <assert.h> 9d6d1731bSAleksandr Iashchenko #include <compiler.h> 10d6d1731bSAleksandr Iashchenko #include <printk.h> 11d6d1731bSAleksandr Iashchenko #include <setjmp.h> 12d6d1731bSAleksandr Iashchenko #include <string.h> 13d6d1731bSAleksandr Iashchenko #include <trace.h> 14d6d1731bSAleksandr Iashchenko #include <types_ext.h> 15d6d1731bSAleksandr Iashchenko #include <util.h> 16d6d1731bSAleksandr Iashchenko 17d6d1731bSAleksandr Iashchenko #if __GCC_VERSION >= 70000 18d6d1731bSAleksandr Iashchenko #define ASAN_ABI_VERSION 7 19d6d1731bSAleksandr Iashchenko #else 20d6d1731bSAleksandr Iashchenko #define ASAN_ABI_VERSION 6 21d6d1731bSAleksandr Iashchenko #endif 22d6d1731bSAleksandr Iashchenko 234cafd8a3SAleksandr Iashchenko #if defined(__KERNEL__) 244cafd8a3SAleksandr Iashchenko # include <keep.h> 254cafd8a3SAleksandr Iashchenko # include <kernel/panic.h> 264cafd8a3SAleksandr Iashchenko #elif defined(__LDELF__) 274cafd8a3SAleksandr Iashchenko # include <ldelf_syscalls.h> 284cafd8a3SAleksandr Iashchenko # include <ldelf.h> 294cafd8a3SAleksandr Iashchenko #else 309f2dc7a1SAleksandr Iashchenko # include <tee_internal_api_extensions.h> 319f2dc7a1SAleksandr Iashchenko # include <utee_syscalls.h> 324cafd8a3SAleksandr Iashchenko #endif 334cafd8a3SAleksandr Iashchenko 344cafd8a3SAleksandr Iashchenko #ifndef __KERNEL__ 354cafd8a3SAleksandr Iashchenko /* Stub for non-kernel builds */ 364cafd8a3SAleksandr Iashchenko #define DECLARE_KEEP_INIT(x) 374cafd8a3SAleksandr Iashchenko #endif 384cafd8a3SAleksandr Iashchenko 394cafd8a3SAleksandr Iashchenko #ifndef SMALL_PAGE_SIZE 404cafd8a3SAleksandr Iashchenko #define SMALL_PAGE_SIZE 4096 414cafd8a3SAleksandr Iashchenko #endif 424cafd8a3SAleksandr Iashchenko 43*081fba0cSAleksandr Iashchenko #if TRACE_LEVEL >= TRACE_DEBUG 44*081fba0cSAleksandr Iashchenko #define KASAN_DUMP_SHADOW 45*081fba0cSAleksandr Iashchenko #endif 46*081fba0cSAleksandr Iashchenko 47d6d1731bSAleksandr Iashchenko struct asan_source_location { 48d6d1731bSAleksandr Iashchenko const char *file_name; 49d6d1731bSAleksandr Iashchenko int line_no; 50d6d1731bSAleksandr Iashchenko int column_no; 51d6d1731bSAleksandr Iashchenko }; 52d6d1731bSAleksandr Iashchenko 53d6d1731bSAleksandr Iashchenko struct asan_global { 54d6d1731bSAleksandr Iashchenko uintptr_t beg; 55d6d1731bSAleksandr Iashchenko uintptr_t size; 56d6d1731bSAleksandr Iashchenko uintptr_t size_with_redzone; 57d6d1731bSAleksandr Iashchenko const char *name; 58d6d1731bSAleksandr Iashchenko const char *module_name; 59d6d1731bSAleksandr Iashchenko uintptr_t has_dynamic_init; 60d6d1731bSAleksandr Iashchenko struct asan_source_location *location; 61d6d1731bSAleksandr Iashchenko #if ASAN_ABI_VERSION >= 7 62d6d1731bSAleksandr Iashchenko uintptr_t odr_indicator; 63d6d1731bSAleksandr Iashchenko #endif 64d6d1731bSAleksandr Iashchenko }; 65d6d1731bSAleksandr Iashchenko 664cafd8a3SAleksandr Iashchenko #ifdef __KERNEL__ 6760aa5df7SAleksandr Iashchenko static struct asan_global_info __asan_global_info; 684cafd8a3SAleksandr Iashchenko #endif 6960aa5df7SAleksandr Iashchenko 70d6d1731bSAleksandr Iashchenko static bool asan_active; 71d6d1731bSAleksandr Iashchenko static asan_panic_cb_t asan_panic_cb = asan_panic; 72d6d1731bSAleksandr Iashchenko 734cafd8a3SAleksandr Iashchenko void __noreturn asan_panic(void) 744cafd8a3SAleksandr Iashchenko { 754cafd8a3SAleksandr Iashchenko #if defined(__KERNEL__) 764cafd8a3SAleksandr Iashchenko panic(); 774cafd8a3SAleksandr Iashchenko #elif defined(__LDELF__) 784cafd8a3SAleksandr Iashchenko _ldelf_panic(2); 794cafd8a3SAleksandr Iashchenko #else 809f2dc7a1SAleksandr Iashchenko _utee_panic(TEE_ERROR_GENERIC); 814cafd8a3SAleksandr Iashchenko #endif 824cafd8a3SAleksandr Iashchenko /* 839f2dc7a1SAleksandr Iashchenko * _utee_panic is not marked as noreturn. 844cafd8a3SAleksandr Iashchenko * See _utee_panic prototype in utee_syscalls.h for reasoning. To 854cafd8a3SAleksandr Iashchenko * prevent "‘noreturn’ function does return" warning the while loop 864cafd8a3SAleksandr Iashchenko * is used. 874cafd8a3SAleksandr Iashchenko */ 884cafd8a3SAleksandr Iashchenko while (1) 894cafd8a3SAleksandr Iashchenko ; 904cafd8a3SAleksandr Iashchenko } 914cafd8a3SAleksandr Iashchenko 92d6d1731bSAleksandr Iashchenko static bool addr_crosses_scale_boundary(vaddr_t addr, size_t size) 93d6d1731bSAleksandr Iashchenko { 94d6d1731bSAleksandr Iashchenko return (addr >> ASAN_BLOCK_SHIFT) != 95d6d1731bSAleksandr Iashchenko ((addr + size - 1) >> ASAN_BLOCK_SHIFT); 96d6d1731bSAleksandr Iashchenko } 97d6d1731bSAleksandr Iashchenko 98d6d1731bSAleksandr Iashchenko static int8_t *va_to_shadow(const void *va) 99d6d1731bSAleksandr Iashchenko { 1004cafd8a3SAleksandr Iashchenko #if defined(__KERNEL__) 1014cafd8a3SAleksandr Iashchenko vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + 1024cafd8a3SAleksandr Iashchenko CFG_ASAN_SHADOW_OFFSET; 1034cafd8a3SAleksandr Iashchenko #else 1044cafd8a3SAleksandr Iashchenko vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + 1054cafd8a3SAleksandr Iashchenko CFG_USER_ASAN_SHADOW_OFFSET; 1064cafd8a3SAleksandr Iashchenko #endif 107d6d1731bSAleksandr Iashchenko return (int8_t *)sa; 108d6d1731bSAleksandr Iashchenko } 109d6d1731bSAleksandr Iashchenko 110d6d1731bSAleksandr Iashchenko static size_t va_range_to_shadow_size(const void *begin, const void *end) 111d6d1731bSAleksandr Iashchenko { 112d6d1731bSAleksandr Iashchenko return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE; 113d6d1731bSAleksandr Iashchenko } 114d6d1731bSAleksandr Iashchenko 115d6d1731bSAleksandr Iashchenko static bool va_range_inside_shadow(const void *begin, const void *end) 116d6d1731bSAleksandr Iashchenko { 11760aa5df7SAleksandr Iashchenko struct asan_va_reg *regs = GET_ASAN_INFO()->regs; 118d6d1731bSAleksandr Iashchenko vaddr_t b = (vaddr_t)begin; 119d6d1731bSAleksandr Iashchenko vaddr_t e = (vaddr_t)end; 12060aa5df7SAleksandr Iashchenko unsigned int i = 0; 121d6d1731bSAleksandr Iashchenko 122d6d1731bSAleksandr Iashchenko if (b >= e) 123d6d1731bSAleksandr Iashchenko return false; 12460aa5df7SAleksandr Iashchenko 12560aa5df7SAleksandr Iashchenko for (i = 0; i < GET_ASAN_INFO()->regs_count; i++) { 12660aa5df7SAleksandr Iashchenko if (b >= regs[i].lo && e <= regs[i].hi) { 12760aa5df7SAleksandr Iashchenko /* Access is covered fully by at least one region */ 12860aa5df7SAleksandr Iashchenko return true; 12960aa5df7SAleksandr Iashchenko } 13060aa5df7SAleksandr Iashchenko } 13160aa5df7SAleksandr Iashchenko 13260aa5df7SAleksandr Iashchenko return false; 133d6d1731bSAleksandr Iashchenko } 134d6d1731bSAleksandr Iashchenko 135d6d1731bSAleksandr Iashchenko static bool va_range_outside_shadow(const void *begin, const void *end) 136d6d1731bSAleksandr Iashchenko { 13760aa5df7SAleksandr Iashchenko struct asan_va_reg *regs = GET_ASAN_INFO()->regs; 138d6d1731bSAleksandr Iashchenko vaddr_t b = (vaddr_t)begin; 139d6d1731bSAleksandr Iashchenko vaddr_t e = (vaddr_t)end; 14060aa5df7SAleksandr Iashchenko unsigned int i = 0; 141d6d1731bSAleksandr Iashchenko 142d6d1731bSAleksandr Iashchenko if (b >= e) 143d6d1731bSAleksandr Iashchenko return false; 14460aa5df7SAleksandr Iashchenko 14560aa5df7SAleksandr Iashchenko for (i = 0; i < GET_ASAN_INFO()->regs_count; i++) { 14660aa5df7SAleksandr Iashchenko if (b < regs[i].hi && e > regs[i].lo) { 14760aa5df7SAleksandr Iashchenko /* Access covers region at least partly */ 14860aa5df7SAleksandr Iashchenko return false; 14960aa5df7SAleksandr Iashchenko } 15060aa5df7SAleksandr Iashchenko } 15160aa5df7SAleksandr Iashchenko 15260aa5df7SAleksandr Iashchenko return true; 153d6d1731bSAleksandr Iashchenko } 154d6d1731bSAleksandr Iashchenko 155d6d1731bSAleksandr Iashchenko static size_t va_misalignment(const void *va) 156d6d1731bSAleksandr Iashchenko { 157d6d1731bSAleksandr Iashchenko return (vaddr_t)va & ASAN_BLOCK_MASK; 158d6d1731bSAleksandr Iashchenko } 159d6d1731bSAleksandr Iashchenko 160d6d1731bSAleksandr Iashchenko static bool va_is_well_aligned(const void *va) 161d6d1731bSAleksandr Iashchenko { 162d6d1731bSAleksandr Iashchenko return !va_misalignment(va); 163d6d1731bSAleksandr Iashchenko } 164d6d1731bSAleksandr Iashchenko 16560aa5df7SAleksandr Iashchenko void asan_add_shadowed(const void *begin, const void *end) 166d6d1731bSAleksandr Iashchenko { 16760aa5df7SAleksandr Iashchenko struct asan_va_reg reg = {(vaddr_t)begin, (vaddr_t)end}; 16860aa5df7SAleksandr Iashchenko struct asan_global_info *asan_info = GET_ASAN_INFO(); 169d6d1731bSAleksandr Iashchenko 170d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 171d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(end)); 17260aa5df7SAleksandr Iashchenko assert(reg.lo < reg.hi); 17360aa5df7SAleksandr Iashchenko if (asan_info->regs_count < ASAN_VA_REGS_MAX) { 17460aa5df7SAleksandr Iashchenko asan_info->regs[asan_info->regs_count++] = reg; 17560aa5df7SAleksandr Iashchenko } else { 17660aa5df7SAleksandr Iashchenko EMSG("No free regions to allocate"); 17760aa5df7SAleksandr Iashchenko asan_panic(); 17860aa5df7SAleksandr Iashchenko } 179d6d1731bSAleksandr Iashchenko } 180d6d1731bSAleksandr Iashchenko 181d6d1731bSAleksandr Iashchenko void asan_tag_no_access(const void *begin, const void *end) 182d6d1731bSAleksandr Iashchenko { 183d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 184d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(end)); 185d6d1731bSAleksandr Iashchenko assert(va_range_inside_shadow(begin, end)); 186d6d1731bSAleksandr Iashchenko 187d6d1731bSAleksandr Iashchenko asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE, 188d6d1731bSAleksandr Iashchenko va_range_to_shadow_size(begin, end)); 189d6d1731bSAleksandr Iashchenko } 190d6d1731bSAleksandr Iashchenko 191d6d1731bSAleksandr Iashchenko void asan_tag_access(const void *begin, const void *end) 192d6d1731bSAleksandr Iashchenko { 19360aa5df7SAleksandr Iashchenko if (!GET_ASAN_INFO()->regs_count || begin == end) 194d6d1731bSAleksandr Iashchenko return; 195d6d1731bSAleksandr Iashchenko 196d6d1731bSAleksandr Iashchenko assert(va_range_inside_shadow(begin, end)); 197d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 198d6d1731bSAleksandr Iashchenko 199d6d1731bSAleksandr Iashchenko asan_memset_unchecked(va_to_shadow(begin), 0, 200d6d1731bSAleksandr Iashchenko va_range_to_shadow_size(begin, end)); 201d6d1731bSAleksandr Iashchenko if (!va_is_well_aligned(end)) 202d6d1731bSAleksandr Iashchenko *va_to_shadow(end) = va_misalignment(end); 203d6d1731bSAleksandr Iashchenko } 204d6d1731bSAleksandr Iashchenko 205d6d1731bSAleksandr Iashchenko void asan_tag_heap_free(const void *begin, const void *end) 206d6d1731bSAleksandr Iashchenko { 20760aa5df7SAleksandr Iashchenko if (!GET_ASAN_INFO()->regs_count) 208d6d1731bSAleksandr Iashchenko return; 209d6d1731bSAleksandr Iashchenko 210d6d1731bSAleksandr Iashchenko assert(va_range_inside_shadow(begin, end)); 211d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 212d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(end)); 213d6d1731bSAleksandr Iashchenko 214d6d1731bSAleksandr Iashchenko asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE, 215d6d1731bSAleksandr Iashchenko va_range_to_shadow_size(begin, end)); 216d6d1731bSAleksandr Iashchenko } 217d6d1731bSAleksandr Iashchenko 218d6d1731bSAleksandr Iashchenko __inhibit_loop_to_libcall void *asan_memset_unchecked(void *s, int c, size_t n) 219d6d1731bSAleksandr Iashchenko { 220d6d1731bSAleksandr Iashchenko uint8_t *b = s; 221d6d1731bSAleksandr Iashchenko size_t m; 222d6d1731bSAleksandr Iashchenko 223d6d1731bSAleksandr Iashchenko for (m = 0; m < n; m++) 224d6d1731bSAleksandr Iashchenko b[m] = c; 225d6d1731bSAleksandr Iashchenko 226d6d1731bSAleksandr Iashchenko return s; 227d6d1731bSAleksandr Iashchenko } 228d6d1731bSAleksandr Iashchenko 229d6d1731bSAleksandr Iashchenko __inhibit_loop_to_libcall 230d6d1731bSAleksandr Iashchenko void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src, 231d6d1731bSAleksandr Iashchenko size_t len) 232d6d1731bSAleksandr Iashchenko { 233d6d1731bSAleksandr Iashchenko uint8_t *__restrict d = dst; 234d6d1731bSAleksandr Iashchenko const uint8_t *__restrict s = src; 235d6d1731bSAleksandr Iashchenko size_t n; 236d6d1731bSAleksandr Iashchenko 237d6d1731bSAleksandr Iashchenko for (n = 0; n < len; n++) 238d6d1731bSAleksandr Iashchenko d[n] = s[n]; 239d6d1731bSAleksandr Iashchenko 240d6d1731bSAleksandr Iashchenko return dst; 241d6d1731bSAleksandr Iashchenko } 242d6d1731bSAleksandr Iashchenko 243d6d1731bSAleksandr Iashchenko void asan_start(void) 244d6d1731bSAleksandr Iashchenko { 24560aa5df7SAleksandr Iashchenko assert(GET_ASAN_INFO()->regs_count > 0 && !asan_active); 246d6d1731bSAleksandr Iashchenko asan_active = true; 247d6d1731bSAleksandr Iashchenko } 248d6d1731bSAleksandr Iashchenko 249d6d1731bSAleksandr Iashchenko void asan_set_panic_cb(asan_panic_cb_t panic_cb) 250d6d1731bSAleksandr Iashchenko { 251d6d1731bSAleksandr Iashchenko asan_panic_cb = panic_cb; 252d6d1731bSAleksandr Iashchenko } 253d6d1731bSAleksandr Iashchenko 254d6d1731bSAleksandr Iashchenko static void asan_report(vaddr_t addr, size_t size) 255d6d1731bSAleksandr Iashchenko { 256d6d1731bSAleksandr Iashchenko #ifdef KASAN_DUMP_SHADOW 257d6d1731bSAleksandr Iashchenko char buf[128] = {0}; 258d6d1731bSAleksandr Iashchenko int r = 0, rc = 0; 259d6d1731bSAleksandr Iashchenko vaddr_t b = 0, e = 0, saddr = 0; 260d6d1731bSAleksandr Iashchenko 261*081fba0cSAleksandr Iashchenko b = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) - ASAN_BLOCK_SIZE * 2; 262*081fba0cSAleksandr Iashchenko e = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) + ASAN_BLOCK_SIZE * 2; 263d6d1731bSAleksandr Iashchenko 264d6d1731bSAleksandr Iashchenko /* Print shadow map nearby */ 265d6d1731bSAleksandr Iashchenko if (va_range_inside_shadow((void *)b, (void *)e)) { 266d6d1731bSAleksandr Iashchenko rc = snprintk(buf + r, sizeof(buf) - r, "%lx: ", b); 267d6d1731bSAleksandr Iashchenko assert(rc > 0); 268d6d1731bSAleksandr Iashchenko r += rc; 269d6d1731bSAleksandr Iashchenko for (saddr = b; saddr <= e; saddr += ASAN_BLOCK_SIZE) { 270d6d1731bSAleksandr Iashchenko int8_t *sbyte = va_to_shadow((void *)saddr); 271d6d1731bSAleksandr Iashchenko 272d6d1731bSAleksandr Iashchenko rc = snprintk(buf + r, sizeof(buf) - r, 273d6d1731bSAleksandr Iashchenko "0x%02x ", (uint8_t)*sbyte); 274d6d1731bSAleksandr Iashchenko assert(rc > 0); 275d6d1731bSAleksandr Iashchenko r += rc; 276d6d1731bSAleksandr Iashchenko } 277d6d1731bSAleksandr Iashchenko EMSG("%s", buf); 278d6d1731bSAleksandr Iashchenko } 279d6d1731bSAleksandr Iashchenko #endif 280*081fba0cSAleksandr Iashchenko EMSG("[ASAN]: access violation, addr: %#"PRIxVA" size: %zu", 281d6d1731bSAleksandr Iashchenko addr, size); 282d6d1731bSAleksandr Iashchenko 283d6d1731bSAleksandr Iashchenko asan_panic_cb(); 284d6d1731bSAleksandr Iashchenko } 285d6d1731bSAleksandr Iashchenko 286d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_1byte_isvalid(vaddr_t addr) 287d6d1731bSAleksandr Iashchenko { 288d6d1731bSAleksandr Iashchenko int8_t last = (addr & ASAN_BLOCK_MASK) + 1; 289d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 290d6d1731bSAleksandr Iashchenko 291d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 292d6d1731bSAleksandr Iashchenko return true; 293d6d1731bSAleksandr Iashchenko 294d6d1731bSAleksandr Iashchenko return false; 295d6d1731bSAleksandr Iashchenko } 296d6d1731bSAleksandr Iashchenko 297d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_2byte_isvalid(vaddr_t addr) 298d6d1731bSAleksandr Iashchenko { 299d6d1731bSAleksandr Iashchenko if (addr_crosses_scale_boundary(addr, 2)) { 300d6d1731bSAleksandr Iashchenko return (asan_shadow_1byte_isvalid(addr) && 301d6d1731bSAleksandr Iashchenko asan_shadow_1byte_isvalid(addr + 1)); 302d6d1731bSAleksandr Iashchenko } else { 303d6d1731bSAleksandr Iashchenko int8_t last = ((addr + 1) & ASAN_BLOCK_MASK) + 1; 304d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 305d6d1731bSAleksandr Iashchenko 306d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 307d6d1731bSAleksandr Iashchenko return true; 308d6d1731bSAleksandr Iashchenko 309d6d1731bSAleksandr Iashchenko return false; 310d6d1731bSAleksandr Iashchenko } 311d6d1731bSAleksandr Iashchenko } 312d6d1731bSAleksandr Iashchenko 313d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_4byte_isvalid(vaddr_t addr) 314d6d1731bSAleksandr Iashchenko { 315d6d1731bSAleksandr Iashchenko if (addr_crosses_scale_boundary(addr, 4)) { 316d6d1731bSAleksandr Iashchenko return (asan_shadow_2byte_isvalid(addr) && 317d6d1731bSAleksandr Iashchenko asan_shadow_2byte_isvalid(addr + 2)); 318d6d1731bSAleksandr Iashchenko } else { 319d6d1731bSAleksandr Iashchenko int8_t last = ((addr + 3) & ASAN_BLOCK_MASK) + 1; 320d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 321d6d1731bSAleksandr Iashchenko 322d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 323d6d1731bSAleksandr Iashchenko return true; 324d6d1731bSAleksandr Iashchenko 325d6d1731bSAleksandr Iashchenko return false; 326d6d1731bSAleksandr Iashchenko } 327d6d1731bSAleksandr Iashchenko } 328d6d1731bSAleksandr Iashchenko 329d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_8byte_isvalid(vaddr_t addr) 330d6d1731bSAleksandr Iashchenko { 331d6d1731bSAleksandr Iashchenko if (addr_crosses_scale_boundary(addr, 8)) { 332d6d1731bSAleksandr Iashchenko return (asan_shadow_4byte_isvalid(addr) && 333d6d1731bSAleksandr Iashchenko asan_shadow_4byte_isvalid(addr + 4)); 334d6d1731bSAleksandr Iashchenko } else { 335d6d1731bSAleksandr Iashchenko int8_t last = ((addr + 7) & ASAN_BLOCK_MASK) + 1; 336d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 337d6d1731bSAleksandr Iashchenko 338d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 339d6d1731bSAleksandr Iashchenko return true; 340d6d1731bSAleksandr Iashchenko 341d6d1731bSAleksandr Iashchenko return false; 342d6d1731bSAleksandr Iashchenko } 343d6d1731bSAleksandr Iashchenko } 344d6d1731bSAleksandr Iashchenko 345d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_Nbyte_isvalid(vaddr_t addr, 346d6d1731bSAleksandr Iashchenko size_t size) 347d6d1731bSAleksandr Iashchenko { 348d6d1731bSAleksandr Iashchenko size_t i = 0; 349d6d1731bSAleksandr Iashchenko 350d6d1731bSAleksandr Iashchenko for (; i < size; i++) { 351d6d1731bSAleksandr Iashchenko if (!asan_shadow_1byte_isvalid(addr + i)) 352d6d1731bSAleksandr Iashchenko return false; 353d6d1731bSAleksandr Iashchenko } 354d6d1731bSAleksandr Iashchenko 355d6d1731bSAleksandr Iashchenko return true; 356d6d1731bSAleksandr Iashchenko } 357d6d1731bSAleksandr Iashchenko 358d6d1731bSAleksandr Iashchenko static __always_inline void check_access(vaddr_t addr, size_t size) 359d6d1731bSAleksandr Iashchenko { 360d6d1731bSAleksandr Iashchenko bool valid = false; 361d6d1731bSAleksandr Iashchenko void *begin = (void *)addr; 362d6d1731bSAleksandr Iashchenko void *end = (void *)(addr + size); 363d6d1731bSAleksandr Iashchenko 364d6d1731bSAleksandr Iashchenko if (!asan_active) 365d6d1731bSAleksandr Iashchenko return; 366d6d1731bSAleksandr Iashchenko if (size == 0) 367d6d1731bSAleksandr Iashchenko return; 368d6d1731bSAleksandr Iashchenko if (va_range_outside_shadow(begin, end)) 369d6d1731bSAleksandr Iashchenko return; 370d6d1731bSAleksandr Iashchenko /* 371d6d1731bSAleksandr Iashchenko * If it isn't outside it has to be completely inside or there's a 372d6d1731bSAleksandr Iashchenko * problem. 373d6d1731bSAleksandr Iashchenko */ 374d6d1731bSAleksandr Iashchenko if (!va_range_inside_shadow(begin, end)) 3754cafd8a3SAleksandr Iashchenko asan_panic(); 376d6d1731bSAleksandr Iashchenko 377d6d1731bSAleksandr Iashchenko if (__builtin_constant_p(size)) { 378d6d1731bSAleksandr Iashchenko switch (size) { 379d6d1731bSAleksandr Iashchenko case 1: 380d6d1731bSAleksandr Iashchenko valid = asan_shadow_1byte_isvalid(addr); 381d6d1731bSAleksandr Iashchenko break; 382d6d1731bSAleksandr Iashchenko case 2: 383d6d1731bSAleksandr Iashchenko valid = asan_shadow_2byte_isvalid(addr); 384d6d1731bSAleksandr Iashchenko break; 385d6d1731bSAleksandr Iashchenko case 4: 386d6d1731bSAleksandr Iashchenko valid = asan_shadow_4byte_isvalid(addr); 387d6d1731bSAleksandr Iashchenko break; 388d6d1731bSAleksandr Iashchenko case 8: 389d6d1731bSAleksandr Iashchenko valid = asan_shadow_8byte_isvalid(addr); 390d6d1731bSAleksandr Iashchenko break; 391d6d1731bSAleksandr Iashchenko default: 392d6d1731bSAleksandr Iashchenko valid = asan_shadow_Nbyte_isvalid(addr, size); 393d6d1731bSAleksandr Iashchenko break; 394d6d1731bSAleksandr Iashchenko } 395d6d1731bSAleksandr Iashchenko } else { 396d6d1731bSAleksandr Iashchenko valid = asan_shadow_Nbyte_isvalid(addr, size); 397d6d1731bSAleksandr Iashchenko } 398d6d1731bSAleksandr Iashchenko 399d6d1731bSAleksandr Iashchenko if (!valid) 400d6d1731bSAleksandr Iashchenko asan_report(addr, size); 401d6d1731bSAleksandr Iashchenko } 402d6d1731bSAleksandr Iashchenko 403d6d1731bSAleksandr Iashchenko static __always_inline void check_load(vaddr_t addr, size_t size) 404d6d1731bSAleksandr Iashchenko { 405d6d1731bSAleksandr Iashchenko check_access(addr, size); 406d6d1731bSAleksandr Iashchenko } 407d6d1731bSAleksandr Iashchenko 408d6d1731bSAleksandr Iashchenko static __always_inline void check_store(vaddr_t addr, size_t size) 409d6d1731bSAleksandr Iashchenko { 410d6d1731bSAleksandr Iashchenko check_access(addr, size); 411d6d1731bSAleksandr Iashchenko } 412d6d1731bSAleksandr Iashchenko 413d6d1731bSAleksandr Iashchenko static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused) 414d6d1731bSAleksandr Iashchenko { 4154cafd8a3SAleksandr Iashchenko asan_panic(); 416d6d1731bSAleksandr Iashchenko } 417d6d1731bSAleksandr Iashchenko 418d6d1731bSAleksandr Iashchenko static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused) 419d6d1731bSAleksandr Iashchenko { 4204cafd8a3SAleksandr Iashchenko asan_panic(); 421d6d1731bSAleksandr Iashchenko } 422d6d1731bSAleksandr Iashchenko 423d6d1731bSAleksandr Iashchenko 424d6d1731bSAleksandr Iashchenko 425d6d1731bSAleksandr Iashchenko #define DEFINE_ASAN_FUNC(type, size) \ 426d6d1731bSAleksandr Iashchenko void __asan_##type##size(vaddr_t addr); \ 427d6d1731bSAleksandr Iashchenko void __asan_##type##size(vaddr_t addr) \ 428d6d1731bSAleksandr Iashchenko { check_##type(addr, size); } \ 429d6d1731bSAleksandr Iashchenko void __asan_##type##size##_noabort(vaddr_t addr); \ 430d6d1731bSAleksandr Iashchenko void __asan_##type##size##_noabort(vaddr_t addr) \ 431d6d1731bSAleksandr Iashchenko { check_##type(addr, size); } \ 432d6d1731bSAleksandr Iashchenko void __asan_report_##type##size##_noabort(vaddr_t addr);\ 433d6d1731bSAleksandr Iashchenko void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \ 434d6d1731bSAleksandr Iashchenko { report_##type(addr, size); } 435d6d1731bSAleksandr Iashchenko 436d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 1) 437d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 2) 438d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 4) 439d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 8) 440d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 16) 441d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 1) 442d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 2) 443d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 4) 444d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 8) 445d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 16) 446d6d1731bSAleksandr Iashchenko 447d6d1731bSAleksandr Iashchenko void __asan_loadN_noabort(vaddr_t addr, size_t size); 448d6d1731bSAleksandr Iashchenko void __asan_loadN_noabort(vaddr_t addr, size_t size) 449d6d1731bSAleksandr Iashchenko { 450d6d1731bSAleksandr Iashchenko check_load(addr, size); 451d6d1731bSAleksandr Iashchenko } 452d6d1731bSAleksandr Iashchenko 453d6d1731bSAleksandr Iashchenko void __asan_storeN_noabort(vaddr_t addr, size_t size); 454d6d1731bSAleksandr Iashchenko void __asan_storeN_noabort(vaddr_t addr, size_t size) 455d6d1731bSAleksandr Iashchenko { 456d6d1731bSAleksandr Iashchenko check_store(addr, size); 457d6d1731bSAleksandr Iashchenko } 458d6d1731bSAleksandr Iashchenko 459d6d1731bSAleksandr Iashchenko void __asan_report_load_n_noabort(vaddr_t addr, size_t size); 460d6d1731bSAleksandr Iashchenko void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size) 461d6d1731bSAleksandr Iashchenko { 462d6d1731bSAleksandr Iashchenko report_load(addr, size); 463d6d1731bSAleksandr Iashchenko } 464d6d1731bSAleksandr Iashchenko 465d6d1731bSAleksandr Iashchenko void __asan_report_store_n_noabort(vaddr_t addr, size_t size); 466d6d1731bSAleksandr Iashchenko void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size) 467d6d1731bSAleksandr Iashchenko { 468d6d1731bSAleksandr Iashchenko report_store(addr, size); 469d6d1731bSAleksandr Iashchenko } 470d6d1731bSAleksandr Iashchenko 471d6d1731bSAleksandr Iashchenko void __asan_handle_no_return(void); 472d6d1731bSAleksandr Iashchenko void __asan_handle_no_return(void) 473d6d1731bSAleksandr Iashchenko { 474d6d1731bSAleksandr Iashchenko } 475d6d1731bSAleksandr Iashchenko 476d6d1731bSAleksandr Iashchenko void __asan_register_globals(struct asan_global *globals, size_t size); 477d6d1731bSAleksandr Iashchenko void __asan_register_globals(struct asan_global *globals, size_t size) 478d6d1731bSAleksandr Iashchenko { 479d6d1731bSAleksandr Iashchenko size_t n = 0; 480d6d1731bSAleksandr Iashchenko 481d6d1731bSAleksandr Iashchenko for (n = 0; n < size; n++) { 482d6d1731bSAleksandr Iashchenko vaddr_t begin = globals[n].beg; 483d6d1731bSAleksandr Iashchenko vaddr_t end = begin + globals[n].size; 484d6d1731bSAleksandr Iashchenko vaddr_t end_align = ROUNDUP(end, ASAN_BLOCK_SIZE); 485d6d1731bSAleksandr Iashchenko vaddr_t end_rz = begin + globals[n].size_with_redzone; 486d6d1731bSAleksandr Iashchenko 487d6d1731bSAleksandr Iashchenko asan_tag_access((void *)begin, (void *)end); 488d6d1731bSAleksandr Iashchenko asan_tag_no_access((void *)end_align, (void *)end_rz); 489d6d1731bSAleksandr Iashchenko } 490d6d1731bSAleksandr Iashchenko } 491d6d1731bSAleksandr Iashchenko DECLARE_KEEP_INIT(__asan_register_globals); 492d6d1731bSAleksandr Iashchenko 493d6d1731bSAleksandr Iashchenko void __asan_unregister_globals(struct asan_global *globals, size_t size); 494d6d1731bSAleksandr Iashchenko void __asan_unregister_globals(struct asan_global *globals __unused, 495d6d1731bSAleksandr Iashchenko size_t size __unused) 496d6d1731bSAleksandr Iashchenko { 497d6d1731bSAleksandr Iashchenko } 498d6d1731bSAleksandr Iashchenko 499d6d1731bSAleksandr Iashchenko void asan_handle_longjmp(void *old_sp) 500d6d1731bSAleksandr Iashchenko { 501d6d1731bSAleksandr Iashchenko void *top = old_sp; 502d6d1731bSAleksandr Iashchenko void *bottom = (void *)ROUNDDOWN((vaddr_t)&top, 503d6d1731bSAleksandr Iashchenko ASAN_BLOCK_SIZE); 504d6d1731bSAleksandr Iashchenko 505d6d1731bSAleksandr Iashchenko asan_tag_access(bottom, top); 506d6d1731bSAleksandr Iashchenko } 5074cafd8a3SAleksandr Iashchenko 5084cafd8a3SAleksandr Iashchenko #if !defined(__KERNEL__) 5094cafd8a3SAleksandr Iashchenko 5104cafd8a3SAleksandr Iashchenko static int asan_map_shadow_region(vaddr_t lo, vaddr_t hi) 5114cafd8a3SAleksandr Iashchenko { 5124cafd8a3SAleksandr Iashchenko struct asan_global_info *asan_info = GET_ASAN_INFO(); 5134cafd8a3SAleksandr Iashchenko TEE_Result rc = TEE_SUCCESS; 5144cafd8a3SAleksandr Iashchenko size_t sz = hi - lo; 5154cafd8a3SAleksandr Iashchenko vaddr_t req = lo; 5164cafd8a3SAleksandr Iashchenko 5174cafd8a3SAleksandr Iashchenko if (asan_info->s_regs_count >= ASAN_VA_REGS_MAX) 5184cafd8a3SAleksandr Iashchenko return -1; 5194cafd8a3SAleksandr Iashchenko 5204cafd8a3SAleksandr Iashchenko #if defined(__LDELF__) 5214cafd8a3SAleksandr Iashchenko rc = _ldelf_map_zi(&req, sz, 0, 0, 0); 5224cafd8a3SAleksandr Iashchenko #else 5239f2dc7a1SAleksandr Iashchenko req = (vaddr_t)tee_map_zi_va(req, sz, 0); 5244cafd8a3SAleksandr Iashchenko #endif 5254cafd8a3SAleksandr Iashchenko if (rc != TEE_SUCCESS) 5264cafd8a3SAleksandr Iashchenko return -1; 5274cafd8a3SAleksandr Iashchenko if (req != lo) 5284cafd8a3SAleksandr Iashchenko return -1; 5294cafd8a3SAleksandr Iashchenko 5304cafd8a3SAleksandr Iashchenko asan_info->s_regs[asan_info->s_regs_count++] = 5314cafd8a3SAleksandr Iashchenko (struct asan_va_reg){ lo, hi }; 5324cafd8a3SAleksandr Iashchenko 5334cafd8a3SAleksandr Iashchenko return 0; 5344cafd8a3SAleksandr Iashchenko } 5354cafd8a3SAleksandr Iashchenko 5364cafd8a3SAleksandr Iashchenko int asan_user_map_shadow(void *lo, void *hi) 5374cafd8a3SAleksandr Iashchenko { 5384cafd8a3SAleksandr Iashchenko vaddr_t lo_s = 0; 5394cafd8a3SAleksandr Iashchenko vaddr_t hi_s = 0; 5404cafd8a3SAleksandr Iashchenko int rc = 0; 5414cafd8a3SAleksandr Iashchenko 5424cafd8a3SAleksandr Iashchenko if (lo == hi) 5434cafd8a3SAleksandr Iashchenko return -1; 5444cafd8a3SAleksandr Iashchenko 5454cafd8a3SAleksandr Iashchenko lo_s = ROUNDDOWN((vaddr_t)va_to_shadow(lo), SMALL_PAGE_SIZE); 5464cafd8a3SAleksandr Iashchenko hi_s = ROUNDUP((vaddr_t)va_to_shadow(hi), SMALL_PAGE_SIZE); 5474cafd8a3SAleksandr Iashchenko 5484cafd8a3SAleksandr Iashchenko if (lo_s >= hi_s) 5494cafd8a3SAleksandr Iashchenko return -1; 5504cafd8a3SAleksandr Iashchenko if (hi >= (void *)GET_ASAN_INFO()) 5514cafd8a3SAleksandr Iashchenko return -1; 5524cafd8a3SAleksandr Iashchenko 5534cafd8a3SAleksandr Iashchenko /* 5544cafd8a3SAleksandr Iashchenko * Walk the already mapped shadow ranges and trim [lo_s, hi_s) 5554cafd8a3SAleksandr Iashchenko * down to the part that is still missing. 5564cafd8a3SAleksandr Iashchenko */ 5574cafd8a3SAleksandr Iashchenko for (size_t i = 0; i < GET_ASAN_INFO()->s_regs_count; i++) { 5584cafd8a3SAleksandr Iashchenko vaddr_t reg_lo_s = GET_ASAN_INFO()->s_regs[i].lo; 5594cafd8a3SAleksandr Iashchenko vaddr_t reg_hi_s = GET_ASAN_INFO()->s_regs[i].hi; 5604cafd8a3SAleksandr Iashchenko 5614cafd8a3SAleksandr Iashchenko if (reg_hi_s <= lo_s || reg_lo_s >= hi_s) { 5624cafd8a3SAleksandr Iashchenko /* 5634cafd8a3SAleksandr Iashchenko * This mapped range does not intersect the 5644cafd8a3SAleksandr Iashchenko * requested range. Skip it. 5654cafd8a3SAleksandr Iashchenko */ 5664cafd8a3SAleksandr Iashchenko continue; 5674cafd8a3SAleksandr Iashchenko } 5684cafd8a3SAleksandr Iashchenko if (reg_lo_s <= lo_s && reg_hi_s >= hi_s) { 5694cafd8a3SAleksandr Iashchenko /* 5704cafd8a3SAleksandr Iashchenko * The requested shadow range is already fully 5714cafd8a3SAleksandr Iashchenko * mapped, so there is nothing left to do. 5724cafd8a3SAleksandr Iashchenko */ 5734cafd8a3SAleksandr Iashchenko goto out; 5744cafd8a3SAleksandr Iashchenko } 5754cafd8a3SAleksandr Iashchenko if (reg_lo_s <= lo_s && reg_hi_s < hi_s) { 5764cafd8a3SAleksandr Iashchenko /* 5774cafd8a3SAleksandr Iashchenko * The mapped range covers the left side of 5784cafd8a3SAleksandr Iashchenko * the requested range. 5794cafd8a3SAleksandr Iashchenko */ 5804cafd8a3SAleksandr Iashchenko lo_s = reg_hi_s; 5814cafd8a3SAleksandr Iashchenko continue; 5824cafd8a3SAleksandr Iashchenko } 5834cafd8a3SAleksandr Iashchenko if (reg_lo_s > lo_s && reg_hi_s >= hi_s) { 5844cafd8a3SAleksandr Iashchenko /* 5854cafd8a3SAleksandr Iashchenko * The mapped range covers the right side of 5864cafd8a3SAleksandr Iashchenko * the requested range. 5874cafd8a3SAleksandr Iashchenko */ 5884cafd8a3SAleksandr Iashchenko hi_s = reg_lo_s; 5894cafd8a3SAleksandr Iashchenko continue; 5904cafd8a3SAleksandr Iashchenko } 5914cafd8a3SAleksandr Iashchenko /* 5924cafd8a3SAleksandr Iashchenko * If we are here then there is a problem, that shouldn't 5934cafd8a3SAleksandr Iashchenko * happen for valid shadow mapping intervals. 5944cafd8a3SAleksandr Iashchenko */ 5954cafd8a3SAleksandr Iashchenko EMSG("can't handle: reg_lo_s %#"PRIxVA" reg_hi_s %#" 5964cafd8a3SAleksandr Iashchenko PRIxVA" lo_s %#"PRIxVA" hi_s %#"PRIxVA, reg_lo_s, 5974cafd8a3SAleksandr Iashchenko reg_hi_s, lo_s, hi_s); 5984cafd8a3SAleksandr Iashchenko asan_panic(); 5994cafd8a3SAleksandr Iashchenko } 6004cafd8a3SAleksandr Iashchenko /* 6014cafd8a3SAleksandr Iashchenko * If we reach this point, [lo_s, hi_s) is the remaining shadow 6024cafd8a3SAleksandr Iashchenko * gap that still needs to be mapped. 6034cafd8a3SAleksandr Iashchenko */ 6044cafd8a3SAleksandr Iashchenko assert(hi_s > lo_s); 6054cafd8a3SAleksandr Iashchenko rc = asan_map_shadow_region(lo_s, hi_s); 6064cafd8a3SAleksandr Iashchenko if (rc) { 6074cafd8a3SAleksandr Iashchenko EMSG("Failed to map shadow region"); 6084cafd8a3SAleksandr Iashchenko asan_panic(); 6094cafd8a3SAleksandr Iashchenko } 6104cafd8a3SAleksandr Iashchenko out: 6114cafd8a3SAleksandr Iashchenko /* Remember the original VA range as checked by ASan. */ 6124cafd8a3SAleksandr Iashchenko asan_add_shadowed(lo, hi); 6134cafd8a3SAleksandr Iashchenko return 0; 6144cafd8a3SAleksandr Iashchenko } 6154cafd8a3SAleksandr Iashchenko 6164cafd8a3SAleksandr Iashchenko #else 6174cafd8a3SAleksandr Iashchenko 6184cafd8a3SAleksandr Iashchenko int asan_user_map_shadow(void *lo __unused, void *hi __unused) 6194cafd8a3SAleksandr Iashchenko { 6204cafd8a3SAleksandr Iashchenko return 0; 6214cafd8a3SAleksandr Iashchenko } 6224cafd8a3SAleksandr Iashchenko #endif 623