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