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