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