1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 */ 5 6 #include <assert.h> 7 #include <compiler.h> 8 #include <keep.h> 9 #include <kernel/asan.h> 10 #include <kernel/panic.h> 11 #include <printk.h> 12 #include <setjmp.h> 13 #include <string.h> 14 #include <trace.h> 15 #include <types_ext.h> 16 #include <util.h> 17 18 #if __GCC_VERSION >= 70000 19 #define ASAN_ABI_VERSION 7 20 #else 21 #define ASAN_ABI_VERSION 6 22 #endif 23 24 struct asan_source_location { 25 const char *file_name; 26 int line_no; 27 int column_no; 28 }; 29 30 struct asan_global { 31 uintptr_t beg; 32 uintptr_t size; 33 uintptr_t size_with_redzone; 34 const char *name; 35 const char *module_name; 36 uintptr_t has_dynamic_init; 37 struct asan_source_location *location; 38 #if ASAN_ABI_VERSION >= 7 39 uintptr_t odr_indicator; 40 #endif 41 }; 42 43 static vaddr_t asan_va_base; 44 static size_t asan_va_size; 45 static bool asan_active; 46 static asan_panic_cb_t asan_panic_cb = asan_panic; 47 48 static int8_t *va_to_shadow(const void *va) 49 { 50 vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET; 51 52 return (int8_t *)sa; 53 } 54 55 static size_t va_range_to_shadow_size(const void *begin, const void *end) 56 { 57 return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE; 58 } 59 60 static bool va_range_inside_shadow(const void *begin, const void *end) 61 { 62 vaddr_t b = (vaddr_t)begin; 63 vaddr_t e = (vaddr_t)end; 64 65 if (b >= e) 66 return false; 67 return (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size)); 68 } 69 70 static bool va_range_outside_shadow(const void *begin, const void *end) 71 { 72 vaddr_t b = (vaddr_t)begin; 73 vaddr_t e = (vaddr_t)end; 74 75 if (b >= e) 76 return false; 77 return (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size)); 78 } 79 80 static size_t va_misalignment(const void *va) 81 { 82 return (vaddr_t)va & ASAN_BLOCK_MASK; 83 } 84 85 static bool va_is_well_aligned(const void *va) 86 { 87 return !va_misalignment(va); 88 } 89 90 void asan_set_shadowed(const void *begin, const void *end) 91 { 92 vaddr_t b = (vaddr_t)begin; 93 vaddr_t e = (vaddr_t)end; 94 95 assert(!asan_va_base); 96 assert(va_is_well_aligned(begin)); 97 assert(va_is_well_aligned(end)); 98 assert(b < e); 99 100 asan_va_base = b; 101 asan_va_size = e - b; 102 } 103 104 void asan_tag_no_access(const void *begin, const void *end) 105 { 106 assert(va_is_well_aligned(begin)); 107 assert(va_is_well_aligned(end)); 108 assert(va_range_inside_shadow(begin, end)); 109 110 asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE, 111 va_range_to_shadow_size(begin, end)); 112 } 113 114 void asan_tag_access(const void *begin, const void *end) 115 { 116 if (!asan_va_base || (begin == end)) 117 return; 118 119 assert(va_range_inside_shadow(begin, end)); 120 assert(va_is_well_aligned(begin)); 121 122 asan_memset_unchecked(va_to_shadow(begin), 0, 123 va_range_to_shadow_size(begin, end)); 124 if (!va_is_well_aligned(end)) 125 *va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end); 126 } 127 128 void asan_tag_heap_free(const void *begin, const void *end) 129 { 130 if (!asan_va_base) 131 return; 132 133 assert(va_range_inside_shadow(begin, end)); 134 assert(va_is_well_aligned(begin)); 135 assert(va_is_well_aligned(end)); 136 137 asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE, 138 va_range_to_shadow_size(begin, end)); 139 } 140 141 __inhibit_loop_to_libcall void *asan_memset_unchecked(void *s, int c, size_t n) 142 { 143 uint8_t *b = s; 144 size_t m; 145 146 for (m = 0; m < n; m++) 147 b[m] = c; 148 149 return s; 150 } 151 152 __inhibit_loop_to_libcall 153 void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src, 154 size_t len) 155 { 156 uint8_t *__restrict d = dst; 157 const uint8_t *__restrict s = src; 158 size_t n; 159 160 for (n = 0; n < len; n++) 161 d[n] = s[n]; 162 163 return dst; 164 } 165 166 void asan_start(void) 167 { 168 assert(asan_va_base && !asan_active); 169 asan_active = true; 170 } 171 172 void __noreturn asan_panic(void) 173 { 174 panic(); 175 } 176 177 void asan_set_panic_cb(asan_panic_cb_t panic_cb) 178 { 179 asan_panic_cb = panic_cb; 180 } 181 182 static void asan_report(vaddr_t addr, size_t size) 183 { 184 #ifdef KASAN_DUMP_SHADOW 185 char buf[128] = {0}; 186 int r = 0, rc = 0; 187 vaddr_t b = 0, e = 0, saddr = 0; 188 189 b = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) - ASAN_BLOCK_SIZE; 190 e = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) + ASAN_BLOCK_SIZE; 191 192 /* Print shadow map nearby */ 193 if (va_range_inside_shadow((void *)b, (void *)e)) { 194 rc = snprintk(buf + r, sizeof(buf) - r, "%lx: ", b); 195 assert(rc > 0); 196 r += rc; 197 for (saddr = b; saddr <= e; saddr += ASAN_BLOCK_SIZE) { 198 int8_t *sbyte = va_to_shadow((void *)saddr); 199 200 rc = snprintk(buf + r, sizeof(buf) - r, 201 "0x%02x ", (uint8_t)*sbyte); 202 assert(rc > 0); 203 r += rc; 204 } 205 EMSG("%s", buf); 206 } 207 #endif 208 EMSG("[ASAN]: access violation, addr: %lx size: %zu\n", 209 addr, size); 210 211 asan_panic_cb(); 212 } 213 214 static void check_access(vaddr_t addr, size_t size) 215 { 216 void *begin = (void *)addr; 217 void *end = (void *)(addr + size); 218 int8_t *a; 219 int8_t *e; 220 221 if (!asan_active || !size) 222 return; 223 if (va_range_outside_shadow(begin, end)) 224 return; 225 /* 226 * If it isn't outside it has to be completely inside or there's a 227 * problem. 228 */ 229 if (!va_range_inside_shadow(begin, end)) 230 panic(); 231 232 e = va_to_shadow((void *)(addr + size - 1)); 233 for (a = va_to_shadow(begin); a <= e; a++) 234 if (*a < 0) 235 asan_report(addr, size); 236 237 if (!va_is_well_aligned(end) && 238 va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE)) 239 asan_report(addr, size); 240 } 241 242 static void check_load(vaddr_t addr, size_t size) 243 { 244 check_access(addr, size); 245 } 246 247 static void check_store(vaddr_t addr, size_t size) 248 { 249 check_access(addr, size); 250 } 251 252 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused) 253 { 254 panic(); 255 } 256 257 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused) 258 { 259 panic(); 260 } 261 262 263 264 #define DEFINE_ASAN_FUNC(type, size) \ 265 void __asan_##type##size(vaddr_t addr); \ 266 void __asan_##type##size(vaddr_t addr) \ 267 { check_##type(addr, size); } \ 268 void __asan_##type##size##_noabort(vaddr_t addr); \ 269 void __asan_##type##size##_noabort(vaddr_t addr) \ 270 { check_##type(addr, size); } \ 271 void __asan_report_##type##size##_noabort(vaddr_t addr);\ 272 void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \ 273 { report_##type(addr, size); } 274 275 DEFINE_ASAN_FUNC(load, 1) 276 DEFINE_ASAN_FUNC(load, 2) 277 DEFINE_ASAN_FUNC(load, 4) 278 DEFINE_ASAN_FUNC(load, 8) 279 DEFINE_ASAN_FUNC(load, 16) 280 DEFINE_ASAN_FUNC(store, 1) 281 DEFINE_ASAN_FUNC(store, 2) 282 DEFINE_ASAN_FUNC(store, 4) 283 DEFINE_ASAN_FUNC(store, 8) 284 DEFINE_ASAN_FUNC(store, 16) 285 286 void __asan_loadN_noabort(vaddr_t addr, size_t size); 287 void __asan_loadN_noabort(vaddr_t addr, size_t size) 288 { 289 check_load(addr, size); 290 } 291 292 void __asan_storeN_noabort(vaddr_t addr, size_t size); 293 void __asan_storeN_noabort(vaddr_t addr, size_t size) 294 { 295 check_store(addr, size); 296 } 297 298 void __asan_report_load_n_noabort(vaddr_t addr, size_t size); 299 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size) 300 { 301 report_load(addr, size); 302 } 303 304 void __asan_report_store_n_noabort(vaddr_t addr, size_t size); 305 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size) 306 { 307 report_store(addr, size); 308 } 309 310 void __asan_handle_no_return(void); 311 void __asan_handle_no_return(void) 312 { 313 } 314 315 void __asan_register_globals(struct asan_global *globals, size_t size); 316 void __asan_register_globals(struct asan_global *globals, size_t size) 317 { 318 size_t n; 319 320 for (n = 0; n < size; n++) 321 asan_tag_access((void *)globals[n].beg, 322 (void *)(globals[n].beg + globals[n].size)); 323 } 324 DECLARE_KEEP_INIT(__asan_register_globals); 325 326 void __asan_unregister_globals(struct asan_global *globals, size_t size); 327 void __asan_unregister_globals(struct asan_global *globals __unused, 328 size_t size __unused) 329 { 330 } 331 332 void asan_handle_longjmp(void *old_sp) 333 { 334 void *top = old_sp; 335 void *bottom = (void *)ROUNDDOWN((vaddr_t)&top, 336 ASAN_BLOCK_SIZE); 337 338 asan_tag_access(bottom, top); 339 } 340