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