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 30*9f2dc7a1SAleksandr Iashchenko # include <tee_internal_api_extensions.h> 31*9f2dc7a1SAleksandr 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 43d6d1731bSAleksandr Iashchenko struct asan_source_location { 44d6d1731bSAleksandr Iashchenko const char *file_name; 45d6d1731bSAleksandr Iashchenko int line_no; 46d6d1731bSAleksandr Iashchenko int column_no; 47d6d1731bSAleksandr Iashchenko }; 48d6d1731bSAleksandr Iashchenko 49d6d1731bSAleksandr Iashchenko struct asan_global { 50d6d1731bSAleksandr Iashchenko uintptr_t beg; 51d6d1731bSAleksandr Iashchenko uintptr_t size; 52d6d1731bSAleksandr Iashchenko uintptr_t size_with_redzone; 53d6d1731bSAleksandr Iashchenko const char *name; 54d6d1731bSAleksandr Iashchenko const char *module_name; 55d6d1731bSAleksandr Iashchenko uintptr_t has_dynamic_init; 56d6d1731bSAleksandr Iashchenko struct asan_source_location *location; 57d6d1731bSAleksandr Iashchenko #if ASAN_ABI_VERSION >= 7 58d6d1731bSAleksandr Iashchenko uintptr_t odr_indicator; 59d6d1731bSAleksandr Iashchenko #endif 60d6d1731bSAleksandr Iashchenko }; 61d6d1731bSAleksandr Iashchenko 624cafd8a3SAleksandr Iashchenko #ifdef __KERNEL__ 6360aa5df7SAleksandr Iashchenko static struct asan_global_info __asan_global_info; 644cafd8a3SAleksandr Iashchenko #endif 6560aa5df7SAleksandr Iashchenko 66d6d1731bSAleksandr Iashchenko static bool asan_active; 67d6d1731bSAleksandr Iashchenko static asan_panic_cb_t asan_panic_cb = asan_panic; 68d6d1731bSAleksandr Iashchenko 694cafd8a3SAleksandr Iashchenko void __noreturn asan_panic(void) 704cafd8a3SAleksandr Iashchenko { 714cafd8a3SAleksandr Iashchenko #if defined(__KERNEL__) 724cafd8a3SAleksandr Iashchenko panic(); 734cafd8a3SAleksandr Iashchenko #elif defined(__LDELF__) 744cafd8a3SAleksandr Iashchenko _ldelf_panic(2); 754cafd8a3SAleksandr Iashchenko #else 76*9f2dc7a1SAleksandr Iashchenko _utee_panic(TEE_ERROR_GENERIC); 774cafd8a3SAleksandr Iashchenko #endif 784cafd8a3SAleksandr Iashchenko /* 79*9f2dc7a1SAleksandr Iashchenko * _utee_panic is not marked as noreturn. 804cafd8a3SAleksandr Iashchenko * See _utee_panic prototype in utee_syscalls.h for reasoning. To 814cafd8a3SAleksandr Iashchenko * prevent "‘noreturn’ function does return" warning the while loop 824cafd8a3SAleksandr Iashchenko * is used. 834cafd8a3SAleksandr Iashchenko */ 844cafd8a3SAleksandr Iashchenko while (1) 854cafd8a3SAleksandr Iashchenko ; 864cafd8a3SAleksandr Iashchenko } 874cafd8a3SAleksandr Iashchenko 88d6d1731bSAleksandr Iashchenko static bool addr_crosses_scale_boundary(vaddr_t addr, size_t size) 89d6d1731bSAleksandr Iashchenko { 90d6d1731bSAleksandr Iashchenko return (addr >> ASAN_BLOCK_SHIFT) != 91d6d1731bSAleksandr Iashchenko ((addr + size - 1) >> ASAN_BLOCK_SHIFT); 92d6d1731bSAleksandr Iashchenko } 93d6d1731bSAleksandr Iashchenko 94d6d1731bSAleksandr Iashchenko static int8_t *va_to_shadow(const void *va) 95d6d1731bSAleksandr Iashchenko { 964cafd8a3SAleksandr Iashchenko #if defined(__KERNEL__) 974cafd8a3SAleksandr Iashchenko vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + 984cafd8a3SAleksandr Iashchenko CFG_ASAN_SHADOW_OFFSET; 994cafd8a3SAleksandr Iashchenko #else 1004cafd8a3SAleksandr Iashchenko vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + 1014cafd8a3SAleksandr Iashchenko CFG_USER_ASAN_SHADOW_OFFSET; 1024cafd8a3SAleksandr Iashchenko #endif 103d6d1731bSAleksandr Iashchenko return (int8_t *)sa; 104d6d1731bSAleksandr Iashchenko } 105d6d1731bSAleksandr Iashchenko 106d6d1731bSAleksandr Iashchenko static size_t va_range_to_shadow_size(const void *begin, const void *end) 107d6d1731bSAleksandr Iashchenko { 108d6d1731bSAleksandr Iashchenko return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE; 109d6d1731bSAleksandr Iashchenko } 110d6d1731bSAleksandr Iashchenko 111d6d1731bSAleksandr Iashchenko static bool va_range_inside_shadow(const void *begin, const void *end) 112d6d1731bSAleksandr Iashchenko { 11360aa5df7SAleksandr Iashchenko struct asan_va_reg *regs = GET_ASAN_INFO()->regs; 114d6d1731bSAleksandr Iashchenko vaddr_t b = (vaddr_t)begin; 115d6d1731bSAleksandr Iashchenko vaddr_t e = (vaddr_t)end; 11660aa5df7SAleksandr Iashchenko unsigned int i = 0; 117d6d1731bSAleksandr Iashchenko 118d6d1731bSAleksandr Iashchenko if (b >= e) 119d6d1731bSAleksandr Iashchenko return false; 12060aa5df7SAleksandr Iashchenko 12160aa5df7SAleksandr Iashchenko for (i = 0; i < GET_ASAN_INFO()->regs_count; i++) { 12260aa5df7SAleksandr Iashchenko if (b >= regs[i].lo && e <= regs[i].hi) { 12360aa5df7SAleksandr Iashchenko /* Access is covered fully by at least one region */ 12460aa5df7SAleksandr Iashchenko return true; 12560aa5df7SAleksandr Iashchenko } 12660aa5df7SAleksandr Iashchenko } 12760aa5df7SAleksandr Iashchenko 12860aa5df7SAleksandr Iashchenko return false; 129d6d1731bSAleksandr Iashchenko } 130d6d1731bSAleksandr Iashchenko 131d6d1731bSAleksandr Iashchenko static bool va_range_outside_shadow(const void *begin, const void *end) 132d6d1731bSAleksandr Iashchenko { 13360aa5df7SAleksandr Iashchenko struct asan_va_reg *regs = GET_ASAN_INFO()->regs; 134d6d1731bSAleksandr Iashchenko vaddr_t b = (vaddr_t)begin; 135d6d1731bSAleksandr Iashchenko vaddr_t e = (vaddr_t)end; 13660aa5df7SAleksandr Iashchenko unsigned int i = 0; 137d6d1731bSAleksandr Iashchenko 138d6d1731bSAleksandr Iashchenko if (b >= e) 139d6d1731bSAleksandr Iashchenko return false; 14060aa5df7SAleksandr Iashchenko 14160aa5df7SAleksandr Iashchenko for (i = 0; i < GET_ASAN_INFO()->regs_count; i++) { 14260aa5df7SAleksandr Iashchenko if (b < regs[i].hi && e > regs[i].lo) { 14360aa5df7SAleksandr Iashchenko /* Access covers region at least partly */ 14460aa5df7SAleksandr Iashchenko return false; 14560aa5df7SAleksandr Iashchenko } 14660aa5df7SAleksandr Iashchenko } 14760aa5df7SAleksandr Iashchenko 14860aa5df7SAleksandr Iashchenko return true; 149d6d1731bSAleksandr Iashchenko } 150d6d1731bSAleksandr Iashchenko 151d6d1731bSAleksandr Iashchenko static size_t va_misalignment(const void *va) 152d6d1731bSAleksandr Iashchenko { 153d6d1731bSAleksandr Iashchenko return (vaddr_t)va & ASAN_BLOCK_MASK; 154d6d1731bSAleksandr Iashchenko } 155d6d1731bSAleksandr Iashchenko 156d6d1731bSAleksandr Iashchenko static bool va_is_well_aligned(const void *va) 157d6d1731bSAleksandr Iashchenko { 158d6d1731bSAleksandr Iashchenko return !va_misalignment(va); 159d6d1731bSAleksandr Iashchenko } 160d6d1731bSAleksandr Iashchenko 16160aa5df7SAleksandr Iashchenko void asan_add_shadowed(const void *begin, const void *end) 162d6d1731bSAleksandr Iashchenko { 16360aa5df7SAleksandr Iashchenko struct asan_va_reg reg = {(vaddr_t)begin, (vaddr_t)end}; 16460aa5df7SAleksandr Iashchenko struct asan_global_info *asan_info = GET_ASAN_INFO(); 165d6d1731bSAleksandr Iashchenko 166d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 167d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(end)); 16860aa5df7SAleksandr Iashchenko assert(reg.lo < reg.hi); 16960aa5df7SAleksandr Iashchenko if (asan_info->regs_count < ASAN_VA_REGS_MAX) { 17060aa5df7SAleksandr Iashchenko asan_info->regs[asan_info->regs_count++] = reg; 17160aa5df7SAleksandr Iashchenko } else { 17260aa5df7SAleksandr Iashchenko EMSG("No free regions to allocate"); 17360aa5df7SAleksandr Iashchenko asan_panic(); 17460aa5df7SAleksandr Iashchenko } 175d6d1731bSAleksandr Iashchenko } 176d6d1731bSAleksandr Iashchenko 177d6d1731bSAleksandr Iashchenko void asan_tag_no_access(const void *begin, const void *end) 178d6d1731bSAleksandr Iashchenko { 179d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 180d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(end)); 181d6d1731bSAleksandr Iashchenko assert(va_range_inside_shadow(begin, end)); 182d6d1731bSAleksandr Iashchenko 183d6d1731bSAleksandr Iashchenko asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE, 184d6d1731bSAleksandr Iashchenko va_range_to_shadow_size(begin, end)); 185d6d1731bSAleksandr Iashchenko } 186d6d1731bSAleksandr Iashchenko 187d6d1731bSAleksandr Iashchenko void asan_tag_access(const void *begin, const void *end) 188d6d1731bSAleksandr Iashchenko { 18960aa5df7SAleksandr Iashchenko if (!GET_ASAN_INFO()->regs_count || begin == end) 190d6d1731bSAleksandr Iashchenko return; 191d6d1731bSAleksandr Iashchenko 192d6d1731bSAleksandr Iashchenko assert(va_range_inside_shadow(begin, end)); 193d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 194d6d1731bSAleksandr Iashchenko 195d6d1731bSAleksandr Iashchenko asan_memset_unchecked(va_to_shadow(begin), 0, 196d6d1731bSAleksandr Iashchenko va_range_to_shadow_size(begin, end)); 197d6d1731bSAleksandr Iashchenko if (!va_is_well_aligned(end)) 198d6d1731bSAleksandr Iashchenko *va_to_shadow(end) = va_misalignment(end); 199d6d1731bSAleksandr Iashchenko } 200d6d1731bSAleksandr Iashchenko 201d6d1731bSAleksandr Iashchenko void asan_tag_heap_free(const void *begin, const void *end) 202d6d1731bSAleksandr Iashchenko { 20360aa5df7SAleksandr Iashchenko if (!GET_ASAN_INFO()->regs_count) 204d6d1731bSAleksandr Iashchenko return; 205d6d1731bSAleksandr Iashchenko 206d6d1731bSAleksandr Iashchenko assert(va_range_inside_shadow(begin, end)); 207d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(begin)); 208d6d1731bSAleksandr Iashchenko assert(va_is_well_aligned(end)); 209d6d1731bSAleksandr Iashchenko 210d6d1731bSAleksandr Iashchenko asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE, 211d6d1731bSAleksandr Iashchenko va_range_to_shadow_size(begin, end)); 212d6d1731bSAleksandr Iashchenko } 213d6d1731bSAleksandr Iashchenko 214d6d1731bSAleksandr Iashchenko __inhibit_loop_to_libcall void *asan_memset_unchecked(void *s, int c, size_t n) 215d6d1731bSAleksandr Iashchenko { 216d6d1731bSAleksandr Iashchenko uint8_t *b = s; 217d6d1731bSAleksandr Iashchenko size_t m; 218d6d1731bSAleksandr Iashchenko 219d6d1731bSAleksandr Iashchenko for (m = 0; m < n; m++) 220d6d1731bSAleksandr Iashchenko b[m] = c; 221d6d1731bSAleksandr Iashchenko 222d6d1731bSAleksandr Iashchenko return s; 223d6d1731bSAleksandr Iashchenko } 224d6d1731bSAleksandr Iashchenko 225d6d1731bSAleksandr Iashchenko __inhibit_loop_to_libcall 226d6d1731bSAleksandr Iashchenko void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src, 227d6d1731bSAleksandr Iashchenko size_t len) 228d6d1731bSAleksandr Iashchenko { 229d6d1731bSAleksandr Iashchenko uint8_t *__restrict d = dst; 230d6d1731bSAleksandr Iashchenko const uint8_t *__restrict s = src; 231d6d1731bSAleksandr Iashchenko size_t n; 232d6d1731bSAleksandr Iashchenko 233d6d1731bSAleksandr Iashchenko for (n = 0; n < len; n++) 234d6d1731bSAleksandr Iashchenko d[n] = s[n]; 235d6d1731bSAleksandr Iashchenko 236d6d1731bSAleksandr Iashchenko return dst; 237d6d1731bSAleksandr Iashchenko } 238d6d1731bSAleksandr Iashchenko 239d6d1731bSAleksandr Iashchenko void asan_start(void) 240d6d1731bSAleksandr Iashchenko { 24160aa5df7SAleksandr Iashchenko assert(GET_ASAN_INFO()->regs_count > 0 && !asan_active); 242d6d1731bSAleksandr Iashchenko asan_active = true; 243d6d1731bSAleksandr Iashchenko } 244d6d1731bSAleksandr Iashchenko 245d6d1731bSAleksandr Iashchenko void asan_set_panic_cb(asan_panic_cb_t panic_cb) 246d6d1731bSAleksandr Iashchenko { 247d6d1731bSAleksandr Iashchenko asan_panic_cb = panic_cb; 248d6d1731bSAleksandr Iashchenko } 249d6d1731bSAleksandr Iashchenko 250d6d1731bSAleksandr Iashchenko static void asan_report(vaddr_t addr, size_t size) 251d6d1731bSAleksandr Iashchenko { 252d6d1731bSAleksandr Iashchenko #ifdef KASAN_DUMP_SHADOW 253d6d1731bSAleksandr Iashchenko char buf[128] = {0}; 254d6d1731bSAleksandr Iashchenko int r = 0, rc = 0; 255d6d1731bSAleksandr Iashchenko vaddr_t b = 0, e = 0, saddr = 0; 256d6d1731bSAleksandr Iashchenko 257d6d1731bSAleksandr Iashchenko b = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) - ASAN_BLOCK_SIZE; 258d6d1731bSAleksandr Iashchenko e = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) + ASAN_BLOCK_SIZE; 259d6d1731bSAleksandr Iashchenko 260d6d1731bSAleksandr Iashchenko /* Print shadow map nearby */ 261d6d1731bSAleksandr Iashchenko if (va_range_inside_shadow((void *)b, (void *)e)) { 262d6d1731bSAleksandr Iashchenko rc = snprintk(buf + r, sizeof(buf) - r, "%lx: ", b); 263d6d1731bSAleksandr Iashchenko assert(rc > 0); 264d6d1731bSAleksandr Iashchenko r += rc; 265d6d1731bSAleksandr Iashchenko for (saddr = b; saddr <= e; saddr += ASAN_BLOCK_SIZE) { 266d6d1731bSAleksandr Iashchenko int8_t *sbyte = va_to_shadow((void *)saddr); 267d6d1731bSAleksandr Iashchenko 268d6d1731bSAleksandr Iashchenko rc = snprintk(buf + r, sizeof(buf) - r, 269d6d1731bSAleksandr Iashchenko "0x%02x ", (uint8_t)*sbyte); 270d6d1731bSAleksandr Iashchenko assert(rc > 0); 271d6d1731bSAleksandr Iashchenko r += rc; 272d6d1731bSAleksandr Iashchenko } 273d6d1731bSAleksandr Iashchenko EMSG("%s", buf); 274d6d1731bSAleksandr Iashchenko } 275d6d1731bSAleksandr Iashchenko #endif 276d6d1731bSAleksandr Iashchenko EMSG("[ASAN]: access violation, addr: %lx size: %zu\n", 277d6d1731bSAleksandr Iashchenko addr, size); 278d6d1731bSAleksandr Iashchenko 279d6d1731bSAleksandr Iashchenko asan_panic_cb(); 280d6d1731bSAleksandr Iashchenko } 281d6d1731bSAleksandr Iashchenko 282d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_1byte_isvalid(vaddr_t addr) 283d6d1731bSAleksandr Iashchenko { 284d6d1731bSAleksandr Iashchenko int8_t last = (addr & ASAN_BLOCK_MASK) + 1; 285d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 286d6d1731bSAleksandr Iashchenko 287d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 288d6d1731bSAleksandr Iashchenko return true; 289d6d1731bSAleksandr Iashchenko 290d6d1731bSAleksandr Iashchenko return false; 291d6d1731bSAleksandr Iashchenko } 292d6d1731bSAleksandr Iashchenko 293d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_2byte_isvalid(vaddr_t addr) 294d6d1731bSAleksandr Iashchenko { 295d6d1731bSAleksandr Iashchenko if (addr_crosses_scale_boundary(addr, 2)) { 296d6d1731bSAleksandr Iashchenko return (asan_shadow_1byte_isvalid(addr) && 297d6d1731bSAleksandr Iashchenko asan_shadow_1byte_isvalid(addr + 1)); 298d6d1731bSAleksandr Iashchenko } else { 299d6d1731bSAleksandr Iashchenko int8_t last = ((addr + 1) & ASAN_BLOCK_MASK) + 1; 300d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 301d6d1731bSAleksandr Iashchenko 302d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 303d6d1731bSAleksandr Iashchenko return true; 304d6d1731bSAleksandr Iashchenko 305d6d1731bSAleksandr Iashchenko return false; 306d6d1731bSAleksandr Iashchenko } 307d6d1731bSAleksandr Iashchenko } 308d6d1731bSAleksandr Iashchenko 309d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_4byte_isvalid(vaddr_t addr) 310d6d1731bSAleksandr Iashchenko { 311d6d1731bSAleksandr Iashchenko if (addr_crosses_scale_boundary(addr, 4)) { 312d6d1731bSAleksandr Iashchenko return (asan_shadow_2byte_isvalid(addr) && 313d6d1731bSAleksandr Iashchenko asan_shadow_2byte_isvalid(addr + 2)); 314d6d1731bSAleksandr Iashchenko } else { 315d6d1731bSAleksandr Iashchenko int8_t last = ((addr + 3) & ASAN_BLOCK_MASK) + 1; 316d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 317d6d1731bSAleksandr Iashchenko 318d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 319d6d1731bSAleksandr Iashchenko return true; 320d6d1731bSAleksandr Iashchenko 321d6d1731bSAleksandr Iashchenko return false; 322d6d1731bSAleksandr Iashchenko } 323d6d1731bSAleksandr Iashchenko } 324d6d1731bSAleksandr Iashchenko 325d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_8byte_isvalid(vaddr_t addr) 326d6d1731bSAleksandr Iashchenko { 327d6d1731bSAleksandr Iashchenko if (addr_crosses_scale_boundary(addr, 8)) { 328d6d1731bSAleksandr Iashchenko return (asan_shadow_4byte_isvalid(addr) && 329d6d1731bSAleksandr Iashchenko asan_shadow_4byte_isvalid(addr + 4)); 330d6d1731bSAleksandr Iashchenko } else { 331d6d1731bSAleksandr Iashchenko int8_t last = ((addr + 7) & ASAN_BLOCK_MASK) + 1; 332d6d1731bSAleksandr Iashchenko int8_t *byte = va_to_shadow((void *)addr); 333d6d1731bSAleksandr Iashchenko 334d6d1731bSAleksandr Iashchenko if (*byte == 0 || last <= *byte) 335d6d1731bSAleksandr Iashchenko return true; 336d6d1731bSAleksandr Iashchenko 337d6d1731bSAleksandr Iashchenko return false; 338d6d1731bSAleksandr Iashchenko } 339d6d1731bSAleksandr Iashchenko } 340d6d1731bSAleksandr Iashchenko 341d6d1731bSAleksandr Iashchenko static __always_inline bool asan_shadow_Nbyte_isvalid(vaddr_t addr, 342d6d1731bSAleksandr Iashchenko size_t size) 343d6d1731bSAleksandr Iashchenko { 344d6d1731bSAleksandr Iashchenko size_t i = 0; 345d6d1731bSAleksandr Iashchenko 346d6d1731bSAleksandr Iashchenko for (; i < size; i++) { 347d6d1731bSAleksandr Iashchenko if (!asan_shadow_1byte_isvalid(addr + i)) 348d6d1731bSAleksandr Iashchenko return false; 349d6d1731bSAleksandr Iashchenko } 350d6d1731bSAleksandr Iashchenko 351d6d1731bSAleksandr Iashchenko return true; 352d6d1731bSAleksandr Iashchenko } 353d6d1731bSAleksandr Iashchenko 354d6d1731bSAleksandr Iashchenko static __always_inline void check_access(vaddr_t addr, size_t size) 355d6d1731bSAleksandr Iashchenko { 356d6d1731bSAleksandr Iashchenko bool valid = false; 357d6d1731bSAleksandr Iashchenko void *begin = (void *)addr; 358d6d1731bSAleksandr Iashchenko void *end = (void *)(addr + size); 359d6d1731bSAleksandr Iashchenko 360d6d1731bSAleksandr Iashchenko if (!asan_active) 361d6d1731bSAleksandr Iashchenko return; 362d6d1731bSAleksandr Iashchenko if (size == 0) 363d6d1731bSAleksandr Iashchenko return; 364d6d1731bSAleksandr Iashchenko if (va_range_outside_shadow(begin, end)) 365d6d1731bSAleksandr Iashchenko return; 366d6d1731bSAleksandr Iashchenko /* 367d6d1731bSAleksandr Iashchenko * If it isn't outside it has to be completely inside or there's a 368d6d1731bSAleksandr Iashchenko * problem. 369d6d1731bSAleksandr Iashchenko */ 370d6d1731bSAleksandr Iashchenko if (!va_range_inside_shadow(begin, end)) 3714cafd8a3SAleksandr Iashchenko asan_panic(); 372d6d1731bSAleksandr Iashchenko 373d6d1731bSAleksandr Iashchenko if (__builtin_constant_p(size)) { 374d6d1731bSAleksandr Iashchenko switch (size) { 375d6d1731bSAleksandr Iashchenko case 1: 376d6d1731bSAleksandr Iashchenko valid = asan_shadow_1byte_isvalid(addr); 377d6d1731bSAleksandr Iashchenko break; 378d6d1731bSAleksandr Iashchenko case 2: 379d6d1731bSAleksandr Iashchenko valid = asan_shadow_2byte_isvalid(addr); 380d6d1731bSAleksandr Iashchenko break; 381d6d1731bSAleksandr Iashchenko case 4: 382d6d1731bSAleksandr Iashchenko valid = asan_shadow_4byte_isvalid(addr); 383d6d1731bSAleksandr Iashchenko break; 384d6d1731bSAleksandr Iashchenko case 8: 385d6d1731bSAleksandr Iashchenko valid = asan_shadow_8byte_isvalid(addr); 386d6d1731bSAleksandr Iashchenko break; 387d6d1731bSAleksandr Iashchenko default: 388d6d1731bSAleksandr Iashchenko valid = asan_shadow_Nbyte_isvalid(addr, size); 389d6d1731bSAleksandr Iashchenko break; 390d6d1731bSAleksandr Iashchenko } 391d6d1731bSAleksandr Iashchenko } else { 392d6d1731bSAleksandr Iashchenko valid = asan_shadow_Nbyte_isvalid(addr, size); 393d6d1731bSAleksandr Iashchenko } 394d6d1731bSAleksandr Iashchenko 395d6d1731bSAleksandr Iashchenko if (!valid) 396d6d1731bSAleksandr Iashchenko asan_report(addr, size); 397d6d1731bSAleksandr Iashchenko } 398d6d1731bSAleksandr Iashchenko 399d6d1731bSAleksandr Iashchenko static __always_inline void check_load(vaddr_t addr, size_t size) 400d6d1731bSAleksandr Iashchenko { 401d6d1731bSAleksandr Iashchenko check_access(addr, size); 402d6d1731bSAleksandr Iashchenko } 403d6d1731bSAleksandr Iashchenko 404d6d1731bSAleksandr Iashchenko static __always_inline void check_store(vaddr_t addr, size_t size) 405d6d1731bSAleksandr Iashchenko { 406d6d1731bSAleksandr Iashchenko check_access(addr, size); 407d6d1731bSAleksandr Iashchenko } 408d6d1731bSAleksandr Iashchenko 409d6d1731bSAleksandr Iashchenko static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused) 410d6d1731bSAleksandr Iashchenko { 4114cafd8a3SAleksandr Iashchenko asan_panic(); 412d6d1731bSAleksandr Iashchenko } 413d6d1731bSAleksandr Iashchenko 414d6d1731bSAleksandr Iashchenko static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused) 415d6d1731bSAleksandr Iashchenko { 4164cafd8a3SAleksandr Iashchenko asan_panic(); 417d6d1731bSAleksandr Iashchenko } 418d6d1731bSAleksandr Iashchenko 419d6d1731bSAleksandr Iashchenko 420d6d1731bSAleksandr Iashchenko 421d6d1731bSAleksandr Iashchenko #define DEFINE_ASAN_FUNC(type, size) \ 422d6d1731bSAleksandr Iashchenko void __asan_##type##size(vaddr_t addr); \ 423d6d1731bSAleksandr Iashchenko void __asan_##type##size(vaddr_t addr) \ 424d6d1731bSAleksandr Iashchenko { check_##type(addr, size); } \ 425d6d1731bSAleksandr Iashchenko void __asan_##type##size##_noabort(vaddr_t addr); \ 426d6d1731bSAleksandr Iashchenko void __asan_##type##size##_noabort(vaddr_t addr) \ 427d6d1731bSAleksandr Iashchenko { check_##type(addr, size); } \ 428d6d1731bSAleksandr Iashchenko void __asan_report_##type##size##_noabort(vaddr_t addr);\ 429d6d1731bSAleksandr Iashchenko void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \ 430d6d1731bSAleksandr Iashchenko { report_##type(addr, size); } 431d6d1731bSAleksandr Iashchenko 432d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 1) 433d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 2) 434d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 4) 435d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 8) 436d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(load, 16) 437d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 1) 438d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 2) 439d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 4) 440d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 8) 441d6d1731bSAleksandr Iashchenko DEFINE_ASAN_FUNC(store, 16) 442d6d1731bSAleksandr Iashchenko 443d6d1731bSAleksandr Iashchenko void __asan_loadN_noabort(vaddr_t addr, size_t size); 444d6d1731bSAleksandr Iashchenko void __asan_loadN_noabort(vaddr_t addr, size_t size) 445d6d1731bSAleksandr Iashchenko { 446d6d1731bSAleksandr Iashchenko check_load(addr, size); 447d6d1731bSAleksandr Iashchenko } 448d6d1731bSAleksandr Iashchenko 449d6d1731bSAleksandr Iashchenko void __asan_storeN_noabort(vaddr_t addr, size_t size); 450d6d1731bSAleksandr Iashchenko void __asan_storeN_noabort(vaddr_t addr, size_t size) 451d6d1731bSAleksandr Iashchenko { 452d6d1731bSAleksandr Iashchenko check_store(addr, size); 453d6d1731bSAleksandr Iashchenko } 454d6d1731bSAleksandr Iashchenko 455d6d1731bSAleksandr Iashchenko void __asan_report_load_n_noabort(vaddr_t addr, size_t size); 456d6d1731bSAleksandr Iashchenko void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size) 457d6d1731bSAleksandr Iashchenko { 458d6d1731bSAleksandr Iashchenko report_load(addr, size); 459d6d1731bSAleksandr Iashchenko } 460d6d1731bSAleksandr Iashchenko 461d6d1731bSAleksandr Iashchenko void __asan_report_store_n_noabort(vaddr_t addr, size_t size); 462d6d1731bSAleksandr Iashchenko void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size) 463d6d1731bSAleksandr Iashchenko { 464d6d1731bSAleksandr Iashchenko report_store(addr, size); 465d6d1731bSAleksandr Iashchenko } 466d6d1731bSAleksandr Iashchenko 467d6d1731bSAleksandr Iashchenko void __asan_handle_no_return(void); 468d6d1731bSAleksandr Iashchenko void __asan_handle_no_return(void) 469d6d1731bSAleksandr Iashchenko { 470d6d1731bSAleksandr Iashchenko } 471d6d1731bSAleksandr Iashchenko 472d6d1731bSAleksandr Iashchenko void __asan_register_globals(struct asan_global *globals, size_t size); 473d6d1731bSAleksandr Iashchenko void __asan_register_globals(struct asan_global *globals, size_t size) 474d6d1731bSAleksandr Iashchenko { 475d6d1731bSAleksandr Iashchenko size_t n = 0; 476d6d1731bSAleksandr Iashchenko 477d6d1731bSAleksandr Iashchenko for (n = 0; n < size; n++) { 478d6d1731bSAleksandr Iashchenko vaddr_t begin = globals[n].beg; 479d6d1731bSAleksandr Iashchenko vaddr_t end = begin + globals[n].size; 480d6d1731bSAleksandr Iashchenko vaddr_t end_align = ROUNDUP(end, ASAN_BLOCK_SIZE); 481d6d1731bSAleksandr Iashchenko vaddr_t end_rz = begin + globals[n].size_with_redzone; 482d6d1731bSAleksandr Iashchenko 483d6d1731bSAleksandr Iashchenko asan_tag_access((void *)begin, (void *)end); 484d6d1731bSAleksandr Iashchenko asan_tag_no_access((void *)end_align, (void *)end_rz); 485d6d1731bSAleksandr Iashchenko } 486d6d1731bSAleksandr Iashchenko } 487d6d1731bSAleksandr Iashchenko DECLARE_KEEP_INIT(__asan_register_globals); 488d6d1731bSAleksandr Iashchenko 489d6d1731bSAleksandr Iashchenko void __asan_unregister_globals(struct asan_global *globals, size_t size); 490d6d1731bSAleksandr Iashchenko void __asan_unregister_globals(struct asan_global *globals __unused, 491d6d1731bSAleksandr Iashchenko size_t size __unused) 492d6d1731bSAleksandr Iashchenko { 493d6d1731bSAleksandr Iashchenko } 494d6d1731bSAleksandr Iashchenko 495d6d1731bSAleksandr Iashchenko void asan_handle_longjmp(void *old_sp) 496d6d1731bSAleksandr Iashchenko { 497d6d1731bSAleksandr Iashchenko void *top = old_sp; 498d6d1731bSAleksandr Iashchenko void *bottom = (void *)ROUNDDOWN((vaddr_t)&top, 499d6d1731bSAleksandr Iashchenko ASAN_BLOCK_SIZE); 500d6d1731bSAleksandr Iashchenko 501d6d1731bSAleksandr Iashchenko asan_tag_access(bottom, top); 502d6d1731bSAleksandr Iashchenko } 5034cafd8a3SAleksandr Iashchenko 5044cafd8a3SAleksandr Iashchenko #if !defined(__KERNEL__) 5054cafd8a3SAleksandr Iashchenko 5064cafd8a3SAleksandr Iashchenko static int asan_map_shadow_region(vaddr_t lo, vaddr_t hi) 5074cafd8a3SAleksandr Iashchenko { 5084cafd8a3SAleksandr Iashchenko struct asan_global_info *asan_info = GET_ASAN_INFO(); 5094cafd8a3SAleksandr Iashchenko TEE_Result rc = TEE_SUCCESS; 5104cafd8a3SAleksandr Iashchenko size_t sz = hi - lo; 5114cafd8a3SAleksandr Iashchenko vaddr_t req = lo; 5124cafd8a3SAleksandr Iashchenko 5134cafd8a3SAleksandr Iashchenko if (asan_info->s_regs_count >= ASAN_VA_REGS_MAX) 5144cafd8a3SAleksandr Iashchenko return -1; 5154cafd8a3SAleksandr Iashchenko 5164cafd8a3SAleksandr Iashchenko #if defined(__LDELF__) 5174cafd8a3SAleksandr Iashchenko rc = _ldelf_map_zi(&req, sz, 0, 0, 0); 5184cafd8a3SAleksandr Iashchenko #else 519*9f2dc7a1SAleksandr Iashchenko req = (vaddr_t)tee_map_zi_va(req, sz, 0); 5204cafd8a3SAleksandr Iashchenko #endif 5214cafd8a3SAleksandr Iashchenko if (rc != TEE_SUCCESS) 5224cafd8a3SAleksandr Iashchenko return -1; 5234cafd8a3SAleksandr Iashchenko if (req != lo) 5244cafd8a3SAleksandr Iashchenko return -1; 5254cafd8a3SAleksandr Iashchenko 5264cafd8a3SAleksandr Iashchenko asan_info->s_regs[asan_info->s_regs_count++] = 5274cafd8a3SAleksandr Iashchenko (struct asan_va_reg){ lo, hi }; 5284cafd8a3SAleksandr Iashchenko 5294cafd8a3SAleksandr Iashchenko return 0; 5304cafd8a3SAleksandr Iashchenko } 5314cafd8a3SAleksandr Iashchenko 5324cafd8a3SAleksandr Iashchenko int asan_user_map_shadow(void *lo, void *hi) 5334cafd8a3SAleksandr Iashchenko { 5344cafd8a3SAleksandr Iashchenko vaddr_t lo_s = 0; 5354cafd8a3SAleksandr Iashchenko vaddr_t hi_s = 0; 5364cafd8a3SAleksandr Iashchenko int rc = 0; 5374cafd8a3SAleksandr Iashchenko 5384cafd8a3SAleksandr Iashchenko if (lo == hi) 5394cafd8a3SAleksandr Iashchenko return -1; 5404cafd8a3SAleksandr Iashchenko 5414cafd8a3SAleksandr Iashchenko lo_s = ROUNDDOWN((vaddr_t)va_to_shadow(lo), SMALL_PAGE_SIZE); 5424cafd8a3SAleksandr Iashchenko hi_s = ROUNDUP((vaddr_t)va_to_shadow(hi), SMALL_PAGE_SIZE); 5434cafd8a3SAleksandr Iashchenko 5444cafd8a3SAleksandr Iashchenko if (lo_s >= hi_s) 5454cafd8a3SAleksandr Iashchenko return -1; 5464cafd8a3SAleksandr Iashchenko if (hi >= (void *)GET_ASAN_INFO()) 5474cafd8a3SAleksandr Iashchenko return -1; 5484cafd8a3SAleksandr Iashchenko 5494cafd8a3SAleksandr Iashchenko /* 5504cafd8a3SAleksandr Iashchenko * Walk the already mapped shadow ranges and trim [lo_s, hi_s) 5514cafd8a3SAleksandr Iashchenko * down to the part that is still missing. 5524cafd8a3SAleksandr Iashchenko */ 5534cafd8a3SAleksandr Iashchenko for (size_t i = 0; i < GET_ASAN_INFO()->s_regs_count; i++) { 5544cafd8a3SAleksandr Iashchenko vaddr_t reg_lo_s = GET_ASAN_INFO()->s_regs[i].lo; 5554cafd8a3SAleksandr Iashchenko vaddr_t reg_hi_s = GET_ASAN_INFO()->s_regs[i].hi; 5564cafd8a3SAleksandr Iashchenko 5574cafd8a3SAleksandr Iashchenko if (reg_hi_s <= lo_s || reg_lo_s >= hi_s) { 5584cafd8a3SAleksandr Iashchenko /* 5594cafd8a3SAleksandr Iashchenko * This mapped range does not intersect the 5604cafd8a3SAleksandr Iashchenko * requested range. Skip it. 5614cafd8a3SAleksandr Iashchenko */ 5624cafd8a3SAleksandr Iashchenko continue; 5634cafd8a3SAleksandr Iashchenko } 5644cafd8a3SAleksandr Iashchenko if (reg_lo_s <= lo_s && reg_hi_s >= hi_s) { 5654cafd8a3SAleksandr Iashchenko /* 5664cafd8a3SAleksandr Iashchenko * The requested shadow range is already fully 5674cafd8a3SAleksandr Iashchenko * mapped, so there is nothing left to do. 5684cafd8a3SAleksandr Iashchenko */ 5694cafd8a3SAleksandr Iashchenko goto out; 5704cafd8a3SAleksandr Iashchenko } 5714cafd8a3SAleksandr Iashchenko if (reg_lo_s <= lo_s && reg_hi_s < hi_s) { 5724cafd8a3SAleksandr Iashchenko /* 5734cafd8a3SAleksandr Iashchenko * The mapped range covers the left side of 5744cafd8a3SAleksandr Iashchenko * the requested range. 5754cafd8a3SAleksandr Iashchenko */ 5764cafd8a3SAleksandr Iashchenko lo_s = reg_hi_s; 5774cafd8a3SAleksandr Iashchenko continue; 5784cafd8a3SAleksandr Iashchenko } 5794cafd8a3SAleksandr Iashchenko if (reg_lo_s > lo_s && reg_hi_s >= hi_s) { 5804cafd8a3SAleksandr Iashchenko /* 5814cafd8a3SAleksandr Iashchenko * The mapped range covers the right side of 5824cafd8a3SAleksandr Iashchenko * the requested range. 5834cafd8a3SAleksandr Iashchenko */ 5844cafd8a3SAleksandr Iashchenko hi_s = reg_lo_s; 5854cafd8a3SAleksandr Iashchenko continue; 5864cafd8a3SAleksandr Iashchenko } 5874cafd8a3SAleksandr Iashchenko /* 5884cafd8a3SAleksandr Iashchenko * If we are here then there is a problem, that shouldn't 5894cafd8a3SAleksandr Iashchenko * happen for valid shadow mapping intervals. 5904cafd8a3SAleksandr Iashchenko */ 5914cafd8a3SAleksandr Iashchenko EMSG("can't handle: reg_lo_s %#"PRIxVA" reg_hi_s %#" 5924cafd8a3SAleksandr Iashchenko PRIxVA" lo_s %#"PRIxVA" hi_s %#"PRIxVA, reg_lo_s, 5934cafd8a3SAleksandr Iashchenko reg_hi_s, lo_s, hi_s); 5944cafd8a3SAleksandr Iashchenko asan_panic(); 5954cafd8a3SAleksandr Iashchenko } 5964cafd8a3SAleksandr Iashchenko /* 5974cafd8a3SAleksandr Iashchenko * If we reach this point, [lo_s, hi_s) is the remaining shadow 5984cafd8a3SAleksandr Iashchenko * gap that still needs to be mapped. 5994cafd8a3SAleksandr Iashchenko */ 6004cafd8a3SAleksandr Iashchenko assert(hi_s > lo_s); 6014cafd8a3SAleksandr Iashchenko rc = asan_map_shadow_region(lo_s, hi_s); 6024cafd8a3SAleksandr Iashchenko if (rc) { 6034cafd8a3SAleksandr Iashchenko EMSG("Failed to map shadow region"); 6044cafd8a3SAleksandr Iashchenko asan_panic(); 6054cafd8a3SAleksandr Iashchenko } 6064cafd8a3SAleksandr Iashchenko out: 6074cafd8a3SAleksandr Iashchenko /* Remember the original VA range as checked by ASan. */ 6084cafd8a3SAleksandr Iashchenko asan_add_shadowed(lo, hi); 6094cafd8a3SAleksandr Iashchenko return 0; 6104cafd8a3SAleksandr Iashchenko } 6114cafd8a3SAleksandr Iashchenko 6124cafd8a3SAleksandr Iashchenko #else 6134cafd8a3SAleksandr Iashchenko 6144cafd8a3SAleksandr Iashchenko int asan_user_map_shadow(void *lo __unused, void *hi __unused) 6154cafd8a3SAleksandr Iashchenko { 6164cafd8a3SAleksandr Iashchenko return 0; 6174cafd8a3SAleksandr Iashchenko } 6184cafd8a3SAleksandr Iashchenko #endif 619