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