asan.c
1 /* SPDX-License-Identifier: GPL-2.0-only */ 2 3 /* 4 * Address sanitizer support. 5 * 6 * Parts of this file are based on mm/kasan 7 * from the Linux kernel 4.19.137. 8 * 9 */ 10 11 #include <console/console.h> 12 #include <symbols.h> 13 #include <assert.h> 14 #include <arch/symbols.h> 15 #include <asan.h> 16 17 static inline void *asan_mem_to_shadow(const void *addr) 18 { 19 #if ENV_SEPARATE_ROMSTAGE 20 return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr - 21 (uintptr_t)&_car_region_start) >> ASAN_SHADOW_SCALE_SHIFT)); 22 #elif ENV_RAMSTAGE 23 return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr - 24 (uintptr_t)&_data) >> ASAN_SHADOW_SCALE_SHIFT)); 25 #endif 26 } 27 28 static inline const void *asan_shadow_to_mem(const void *shadow_addr) 29 { 30 #if ENV_SEPARATE_ROMSTAGE 31 return (void *)((uintptr_t)&_car_region_start + (((uintptr_t)shadow_addr - 32 (uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT)); 33 #elif ENV_RAMSTAGE 34 return (void *)((uintptr_t)&_data + (((uintptr_t)shadow_addr - 35 (uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT)); 36 #endif 37 } 38 39 static void asan_poison_shadow(const void *address, size_t size, u8 value) 40 { 41 void *shadow_start, *shadow_end; 42 43 shadow_start = asan_mem_to_shadow(address); 44 shadow_end = asan_mem_to_shadow(address + size); 45 46 __builtin_memset(shadow_start, value, shadow_end - shadow_start); 47 } 48 49 void asan_unpoison_shadow(const void *address, size_t size) 50 { 51 asan_poison_shadow(address, size, 0); 52 53 if (size & ASAN_SHADOW_MASK) { 54 u8 *shadow = (u8 *)asan_mem_to_shadow(address + size); 55 *shadow = size & ASAN_SHADOW_MASK; 56 } 57 } 58 59 static __always_inline bool memory_is_poisoned_1(unsigned long addr) 60 { 61 s8 shadow_value = *(s8 *)asan_mem_to_shadow((void *)addr); 62 63 if (unlikely(shadow_value)) { 64 s8 last_accessible_byte = addr & ASAN_SHADOW_MASK; 65 return unlikely(last_accessible_byte >= shadow_value); 66 } 67 68 return false; 69 } 70 71 static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr, 72 unsigned long size) 73 { 74 u8 *shadow_addr = (u8 *)asan_mem_to_shadow((void *)addr); 75 76 if (unlikely(((addr + size - 1) & ASAN_SHADOW_MASK) < size - 1)) 77 return *shadow_addr || memory_is_poisoned_1(addr + size - 1); 78 79 return memory_is_poisoned_1(addr + size - 1); 80 } 81 82 static __always_inline bool memory_is_poisoned_16(unsigned long addr) 83 { 84 u16 *shadow_addr = (u16 *)asan_mem_to_shadow((void *)addr); 85 86 if (unlikely(!IS_ALIGNED(addr, ASAN_SHADOW_SCALE_SIZE))) 87 return *shadow_addr || memory_is_poisoned_1(addr + 15); 88 89 return *shadow_addr; 90 } 91 92 static __always_inline unsigned long bytes_is_nonzero(const u8 *start, 93 size_t size) 94 { 95 while (size) { 96 if (unlikely(*start)) 97 return (unsigned long)start; 98 start++; 99 size--; 100 } 101 102 return 0; 103 } 104 105 static __always_inline unsigned long memory_is_nonzero(const void *start, 106 const void *end) 107 { 108 unsigned int words; 109 unsigned long ret; 110 unsigned int prefix = (unsigned long)start % 8; 111 112 if (end - start <= 16) 113 return bytes_is_nonzero(start, end - start); 114 115 if (prefix) { 116 prefix = 8 - prefix; 117 ret = bytes_is_nonzero(start, prefix); 118 if (unlikely(ret)) 119 return ret; 120 start += prefix; 121 } 122 123 words = (end - start) / 8; 124 while (words) { 125 if (unlikely(*(u64 *)start)) 126 return bytes_is_nonzero(start, 8); 127 start += 8; 128 words--; 129 } 130 131 return bytes_is_nonzero(start, (end - start) % 8); 132 } 133 134 static __always_inline bool memory_is_poisoned_n(unsigned long addr, 135 size_t size) 136 { 137 unsigned long ret; 138 139 ret = memory_is_nonzero(asan_mem_to_shadow((void *)addr), 140 asan_mem_to_shadow((void *)addr + size - 1) + 1); 141 142 if (unlikely(ret)) { 143 unsigned long last_byte = addr + size - 1; 144 s8 *last_shadow = (s8 *)asan_mem_to_shadow((void *)last_byte); 145 146 if (unlikely(ret != (unsigned long)last_shadow || 147 ((long)(last_byte & ASAN_SHADOW_MASK) >= *last_shadow))) 148 return true; 149 } 150 return false; 151 } 152 153 static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size) 154 { 155 if (__builtin_constant_p(size)) { 156 switch (size) { 157 case 1: 158 return memory_is_poisoned_1(addr); 159 case 2: 160 case 4: 161 case 8: 162 return memory_is_poisoned_2_4_8(addr, size); 163 case 16: 164 return memory_is_poisoned_16(addr); 165 default: 166 assert(0); 167 } 168 } 169 170 return memory_is_poisoned_n(addr, size); 171 } 172 173 static const void *find_first_bad_addr(const void *addr, size_t size) 174 { 175 u8 shadow_val = *(u8 *)asan_mem_to_shadow(addr); 176 const void *first_bad_addr = addr; 177 178 while (!shadow_val && first_bad_addr < addr + size) { 179 first_bad_addr += ASAN_SHADOW_SCALE_SIZE; 180 shadow_val = *(u8 *)asan_mem_to_shadow(first_bad_addr); 181 } 182 return first_bad_addr; 183 } 184 185 static const char *get_shadow_bug_type(const void *addr, size_t size) 186 { 187 const char *bug_type = "unknown-crash"; 188 u8 *shadow_addr; 189 const void *first_bad_addr; 190 191 if (addr < asan_shadow_to_mem((void *) &_asan_shadow)) 192 return bug_type; 193 194 first_bad_addr = find_first_bad_addr(addr, size); 195 196 shadow_addr = (u8 *)asan_mem_to_shadow(first_bad_addr); 197 198 if (*shadow_addr > 0 && *shadow_addr <= ASAN_SHADOW_SCALE_SIZE - 1) 199 shadow_addr++; 200 201 switch (*shadow_addr) { 202 case 0 ... ASAN_SHADOW_SCALE_SIZE - 1: 203 bug_type = "out-of-bounds"; 204 break; 205 case ASAN_GLOBAL_REDZONE: 206 bug_type = "global-out-of-bounds"; 207 break; 208 case ASAN_STACK_LEFT: 209 case ASAN_STACK_MID: 210 case ASAN_STACK_RIGHT: 211 case ASAN_STACK_PARTIAL: 212 bug_type = "stack-out-of-bounds"; 213 break; 214 case ASAN_USE_AFTER_SCOPE: 215 bug_type = "use-after-scope"; 216 break; 217 default: 218 bug_type = "unknown-crash"; 219 } 220 221 return bug_type; 222 } 223 224 void asan_report(unsigned long addr, size_t size, bool is_write, 225 unsigned long ip) 226 { 227 const char *bug_type = get_shadow_bug_type((void *) addr, size); 228 printk(BIOS_ERR, "\n"); 229 printk(BIOS_ERR, "ASan: %s in %p\n", bug_type, (void *) ip); 230 printk(BIOS_ERR, "%s of %zu byte%s at addr %p\n", 231 is_write ? "Write" : "Read", size, (size > 1 ? "s" : ""), 232 (void *) addr); 233 printk(BIOS_ERR, "\n"); 234 } 235 236 static __always_inline void check_memory_region_inline(unsigned long addr, 237 size_t size, bool write, 238 unsigned long ret_ip) 239 { 240 #if ENV_SEPARATE_ROMSTAGE 241 if (((uintptr_t)addr < (uintptr_t)&_car_region_start) || 242 ((uintptr_t)addr > (uintptr_t)&_ebss)) 243 return; 244 #elif ENV_RAMSTAGE 245 if (((uintptr_t)addr < (uintptr_t)&_data) || 246 ((uintptr_t)addr > (uintptr_t)&_eheap)) 247 return; 248 #endif 249 if (unlikely(size == 0)) 250 return; 251 252 if (unlikely((void *)addr < 253 asan_shadow_to_mem((void *) &_asan_shadow))) { 254 asan_report(addr, size, write, ret_ip); 255 return; 256 } 257 258 if (likely(!memory_is_poisoned(addr, size))) 259 return; 260 261 asan_report(addr, size, write, ret_ip); 262 } 263 264 void check_memory_region(unsigned long addr, size_t size, bool write, 265 unsigned long ret_ip) 266 { 267 check_memory_region_inline(addr, size, write, ret_ip); 268 } 269 270 uintptr_t __asan_shadow_offset(uintptr_t addr) 271 { 272 #if ENV_SEPARATE_ROMSTAGE 273 return (uintptr_t)&_asan_shadow - (((uintptr_t)&_car_region_start) >> 274 ASAN_SHADOW_SCALE_SHIFT); 275 #elif ENV_RAMSTAGE 276 return (uintptr_t)&_asan_shadow - (((uintptr_t)&_data) >> 277 ASAN_SHADOW_SCALE_SHIFT); 278 #endif 279 } 280 281 static void register_global(struct asan_global *global) 282 { 283 size_t aligned_size = ALIGN_UP(global->size, ASAN_SHADOW_SCALE_SIZE); 284 285 asan_unpoison_shadow(global->beg, global->size); 286 287 asan_poison_shadow(global->beg + aligned_size, 288 global->size_with_redzone - aligned_size, 289 ASAN_GLOBAL_REDZONE); 290 } 291 292 void __asan_register_globals(struct asan_global *globals, size_t size) 293 { 294 int i; 295 296 for (i = 0; i < size; i++) 297 register_global(&globals[i]); 298 } 299 300 void __asan_unregister_globals(struct asan_global *globals, size_t size) 301 { 302 } 303 304 /* 305 * GCC adds constructors invoking __asan_register_globals() and passes 306 * information about global variable (address, size, size with redzone ...) 307 * to it so we could poison variable's redzone. 308 * This function calls those constructors. 309 */ 310 #if ENV_RAMSTAGE 311 static void asan_ctors(void) 312 { 313 extern long __CTOR_LIST__; 314 typedef void (*func_ptr)(void); 315 func_ptr *ctor = (func_ptr *) &__CTOR_LIST__; 316 if (ctor == NULL) 317 return; 318 319 for (; *ctor != (func_ptr) 0; ctor++) 320 (*ctor)(); 321 } 322 #endif 323 324 void asan_init(void) 325 { 326 #if ENV_SEPARATE_ROMSTAGE 327 size_t size = (size_t)&_ebss - (size_t)&_car_region_start; 328 asan_unpoison_shadow((void *)&_car_region_start, size); 329 #elif ENV_RAMSTAGE 330 size_t size = (size_t)&_eheap - (size_t)&_data; 331 asan_unpoison_shadow((void *)&_data, size); 332 asan_ctors(); 333 #endif 334 } 335 336 void __asan_poison_stack_memory(const void *addr, size_t size) 337 { 338 asan_poison_shadow(addr, ALIGN_UP(size, ASAN_SHADOW_SCALE_SIZE), 339 ASAN_USE_AFTER_SCOPE); 340 } 341 342 void __asan_unpoison_stack_memory(const void *addr, size_t size) 343 { 344 asan_unpoison_shadow(addr, size); 345 } 346 347 #define DEFINE_ASAN_LOAD_STORE(size) \ 348 void __asan_load##size(unsigned long addr) \ 349 { \ 350 check_memory_region_inline(addr, size, false, _RET_IP_);\ 351 } \ 352 void __asan_load##size##_noabort(unsigned long addr) \ 353 { \ 354 check_memory_region_inline(addr, size, false, _RET_IP_);\ 355 } \ 356 void __asan_store##size(unsigned long addr) \ 357 { \ 358 check_memory_region_inline(addr, size, true, _RET_IP_); \ 359 } \ 360 void __asan_store##size##_noabort(unsigned long addr) \ 361 { \ 362 check_memory_region_inline(addr, size, true, _RET_IP_); \ 363 } 364 365 DEFINE_ASAN_LOAD_STORE(1); 366 DEFINE_ASAN_LOAD_STORE(2); 367 DEFINE_ASAN_LOAD_STORE(4); 368 DEFINE_ASAN_LOAD_STORE(8); 369 DEFINE_ASAN_LOAD_STORE(16); 370 371 void __asan_loadN(unsigned long addr, size_t size) 372 { 373 check_memory_region(addr, size, false, _RET_IP_); 374 } 375 376 void __asan_storeN(unsigned long addr, size_t size) 377 { 378 check_memory_region(addr, size, true, _RET_IP_); 379 } 380 381 void __asan_loadN_noabort(unsigned long addr, size_t size) 382 { 383 check_memory_region(addr, size, false, _RET_IP_); 384 } 385 386 void __asan_storeN_noabort(unsigned long addr, size_t size) 387 { 388 check_memory_region(addr, size, true, _RET_IP_); 389 } 390 391 void __asan_handle_no_return(void) 392 { 393 } 394 395 #define DEFINE_ASAN_SET_SHADOW(byte) \ 396 void __asan_set_shadow_##byte(const void *addr, size_t size) \ 397 { \ 398 __builtin_memset((void *)addr, 0x##byte, size); \ 399 } 400 401 DEFINE_ASAN_SET_SHADOW(00); 402 DEFINE_ASAN_SET_SHADOW(f1); 403 DEFINE_ASAN_SET_SHADOW(f2); 404 DEFINE_ASAN_SET_SHADOW(f3); 405 DEFINE_ASAN_SET_SHADOW(f5); 406 DEFINE_ASAN_SET_SHADOW(f8); 407 408 #define DEFINE_ASAN_REPORT_LOAD(size) \ 409 void __asan_report_load##size##_noabort(unsigned long addr) \ 410 { \ 411 asan_report(addr, size, false, _RET_IP_); \ 412 } 413 414 #define DEFINE_ASAN_REPORT_STORE(size) \ 415 void __asan_report_store##size##_noabort(unsigned long addr) \ 416 { \ 417 asan_report(addr, size, true, _RET_IP_); \ 418 } 419 420 DEFINE_ASAN_REPORT_LOAD(1); 421 DEFINE_ASAN_REPORT_LOAD(2); 422 DEFINE_ASAN_REPORT_LOAD(4); 423 DEFINE_ASAN_REPORT_LOAD(8); 424 DEFINE_ASAN_REPORT_LOAD(16); 425 DEFINE_ASAN_REPORT_STORE(1); 426 DEFINE_ASAN_REPORT_STORE(2); 427 DEFINE_ASAN_REPORT_STORE(4); 428 DEFINE_ASAN_REPORT_STORE(8); 429 DEFINE_ASAN_REPORT_STORE(16); 430 431 void __asan_report_load_n_noabort(unsigned long addr, size_t size) 432 { 433 asan_report(addr, size, false, _RET_IP_); 434 } 435 436 void __asan_report_store_n_noabort(unsigned long addr, size_t size) 437 { 438 asan_report(addr, size, true, _RET_IP_); 439 }