1 // SPDX-License-Identifier: BSD-2-Clause 2 /* 3 * Copyright (c) 2016, Linaro Limited 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions are met: 8 * 9 * 1. Redistributions of source code must retain the above copyright notice, 10 * this list of conditions and the following disclaimer. 11 * 12 * 2. Redistributions in binary form must reproduce the above copyright notice, 13 * this list of conditions and the following disclaimer in the documentation 14 * and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #include <assert.h> 30 #include <compiler.h> 31 #include <keep.h> 32 #include <kernel/asan.h> 33 #include <kernel/panic.h> 34 #include <string.h> 35 #include <trace.h> 36 #include <types_ext.h> 37 #include <util.h> 38 39 struct asan_source_location { 40 const char *file_name; 41 int line_no; 42 int column_no; 43 }; 44 45 struct asan_global { 46 uintptr_t beg; 47 uintptr_t size; 48 uintptr_t size_with_redzone; 49 const char *name; 50 const char *module_name; 51 uintptr_t has_dynamic_init; 52 struct asan_source_location *location; 53 }; 54 55 static vaddr_t asan_va_base; 56 static size_t asan_va_size; 57 static bool asan_active; 58 59 static int8_t *va_to_shadow(const void *va) 60 { 61 vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET; 62 63 return (int8_t *)sa; 64 } 65 66 static size_t va_range_to_shadow_size(const void *begin, const void *end) 67 { 68 return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE; 69 } 70 71 static bool va_range_inside_shadow(const void *begin, const void *end) 72 { 73 vaddr_t b = (vaddr_t)begin; 74 vaddr_t e = (vaddr_t)end; 75 76 if (b >= e) 77 return false; 78 return (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size)); 79 } 80 81 static bool va_range_outside_shadow(const void *begin, const void *end) 82 { 83 vaddr_t b = (vaddr_t)begin; 84 vaddr_t e = (vaddr_t)end; 85 86 if (b >= e) 87 return false; 88 return (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size)); 89 } 90 91 static size_t va_misalignment(const void *va) 92 { 93 return (vaddr_t)va & ASAN_BLOCK_MASK; 94 } 95 96 static bool va_is_well_aligned(const void *va) 97 { 98 return !va_misalignment(va); 99 } 100 101 void asan_set_shadowed(const void *begin, const void *end) 102 { 103 vaddr_t b = (vaddr_t)begin; 104 vaddr_t e = (vaddr_t)end; 105 106 assert(!asan_va_base); 107 assert(va_is_well_aligned(begin)); 108 assert(va_is_well_aligned(end)); 109 assert(b < e); 110 111 asan_va_base = b; 112 asan_va_size = e - b; 113 } 114 115 void asan_tag_no_access(const void *begin, const void *end) 116 { 117 assert(va_is_well_aligned(begin)); 118 assert(va_is_well_aligned(end)); 119 assert(va_range_inside_shadow(begin, end)); 120 121 asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE, 122 va_range_to_shadow_size(begin, end)); 123 } 124 125 void asan_tag_access(const void *begin, const void *end) 126 { 127 if (!asan_va_base || (begin == end)) 128 return; 129 130 assert(va_range_inside_shadow(begin, end)); 131 assert(va_is_well_aligned(begin)); 132 133 asan_memset_unchecked(va_to_shadow(begin), 0, 134 va_range_to_shadow_size(begin, end)); 135 if (!va_is_well_aligned(end)) 136 *va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end); 137 } 138 139 void asan_tag_heap_free(const void *begin, const void *end) 140 { 141 if (!asan_va_base) 142 return; 143 144 assert(va_range_inside_shadow(begin, end)); 145 assert(va_is_well_aligned(begin)); 146 assert(va_is_well_aligned(end)); 147 148 asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE, 149 va_range_to_shadow_size(begin, end)); 150 } 151 152 void *asan_memset_unchecked(void *s, int c, size_t n) 153 { 154 uint8_t *b = s; 155 size_t m; 156 157 for (m = 0; m < n; m++) 158 b[m] = c; 159 160 return s; 161 } 162 163 void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src, 164 size_t len) 165 { 166 uint8_t *__restrict d = dst; 167 const uint8_t *__restrict s = src; 168 size_t n; 169 170 for (n = 0; n < len; n++) 171 d[n] = s[n]; 172 173 return dst; 174 } 175 176 void asan_start(void) 177 { 178 assert(asan_va_base && !asan_active); 179 asan_active = true; 180 } 181 182 static void check_access(vaddr_t addr, size_t size) 183 { 184 void *begin = (void *)addr; 185 void *end = (void *)(addr + size); 186 int8_t *a; 187 int8_t *e; 188 189 if (!asan_active || !size) 190 return; 191 if (va_range_outside_shadow(begin, end)) 192 return; 193 /* 194 * If it isn't outside it has to be completely inside or there's a 195 * problem. 196 */ 197 if (!va_range_inside_shadow(begin, end)) 198 panic(); 199 200 e = va_to_shadow((void *)(addr + size - 1)); 201 for (a = va_to_shadow(begin); a <= e; a++) 202 if (*a < 0) 203 panic(); 204 205 if (!va_is_well_aligned(end) && 206 va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE)) 207 panic(); 208 } 209 210 static void check_load(vaddr_t addr, size_t size) 211 { 212 check_access(addr, size); 213 } 214 215 static void check_store(vaddr_t addr, size_t size) 216 { 217 check_access(addr, size); 218 } 219 220 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused) 221 { 222 panic(); 223 } 224 225 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused) 226 { 227 panic(); 228 } 229 230 231 232 #define DEFINE_ASAN_FUNC(type, size) \ 233 void __asan_##type##size(vaddr_t addr); \ 234 void __asan_##type##size(vaddr_t addr) \ 235 { check_##type(addr, size); } \ 236 void __asan_##type##size##_noabort(vaddr_t addr); \ 237 void __asan_##type##size##_noabort(vaddr_t addr) \ 238 { check_##type(addr, size); } \ 239 void __asan_report_##type##size##_noabort(vaddr_t addr);\ 240 void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \ 241 { report_##type(addr, size); } 242 243 DEFINE_ASAN_FUNC(load, 1) 244 DEFINE_ASAN_FUNC(load, 2) 245 DEFINE_ASAN_FUNC(load, 4) 246 DEFINE_ASAN_FUNC(load, 8) 247 DEFINE_ASAN_FUNC(load, 16) 248 DEFINE_ASAN_FUNC(store, 1) 249 DEFINE_ASAN_FUNC(store, 2) 250 DEFINE_ASAN_FUNC(store, 4) 251 DEFINE_ASAN_FUNC(store, 8) 252 DEFINE_ASAN_FUNC(store, 16) 253 254 void __asan_loadN_noabort(vaddr_t addr, size_t size); 255 void __asan_loadN_noabort(vaddr_t addr, size_t size) 256 { 257 check_load(addr, size); 258 } 259 260 void __asan_storeN_noabort(vaddr_t addr, size_t size); 261 void __asan_storeN_noabort(vaddr_t addr, size_t size) 262 { 263 check_store(addr, size); 264 } 265 266 void __asan_report_load_n_noabort(vaddr_t addr, size_t size); 267 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size) 268 { 269 report_load(addr, size); 270 } 271 272 void __asan_report_store_n_noabort(vaddr_t addr, size_t size); 273 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size) 274 { 275 report_store(addr, size); 276 } 277 278 void __asan_handle_no_return(void); 279 void __asan_handle_no_return(void) 280 { 281 } 282 283 void __asan_register_globals(struct asan_global *globals, size_t size); 284 void __asan_register_globals(struct asan_global *globals, size_t size) 285 { 286 size_t n; 287 288 for (n = 0; n < size; n++) 289 asan_tag_access((void *)globals[n].beg, 290 (void *)(globals[n].beg + globals[n].size)); 291 } 292 KEEP_INIT(__asan_register_globals); 293 294 void __asan_unregister_globals(struct asan_global *globals, size_t size); 295 void __asan_unregister_globals(struct asan_global *globals __unused, 296 size_t size __unused) 297 { 298 } 299