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
va_to_shadow(const void * va)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
va_range_to_shadow_size(const void * begin,const void * end)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
va_range_inside_shadow(const void * begin,const void * end)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
va_range_outside_shadow(const void * begin,const void * end)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
va_misalignment(const void * va)77 static size_t va_misalignment(const void *va)
78 {
79 return (vaddr_t)va & ASAN_BLOCK_MASK;
80 }
81
va_is_well_aligned(const void * va)82 static bool va_is_well_aligned(const void *va)
83 {
84 return !va_misalignment(va);
85 }
86
asan_set_shadowed(const void * begin,const void * end)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
asan_tag_no_access(const void * begin,const void * end)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
asan_tag_access(const void * begin,const void * end)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
asan_tag_heap_free(const void * begin,const void * end)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
asan_memset_unchecked(void * s,int c,size_t n)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
asan_memcpy_unchecked(void * __restrict dst,const void * __restrict src,size_t len)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
asan_start(void)162 void asan_start(void)
163 {
164 assert(asan_va_base && !asan_active);
165 asan_active = true;
166 }
167
check_access(vaddr_t addr,size_t size)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
check_load(vaddr_t addr,size_t size)196 static void check_load(vaddr_t addr, size_t size)
197 {
198 check_access(addr, size);
199 }
200
check_store(vaddr_t addr,size_t size)201 static void check_store(vaddr_t addr, size_t size)
202 {
203 check_access(addr, size);
204 }
205
report_load(vaddr_t addr __unused,size_t size __unused)206 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
207 {
208 panic();
209 }
210
report_store(vaddr_t addr __unused,size_t size __unused)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);
__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);
__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);
__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);
__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);
__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);
__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);
__asan_unregister_globals(struct asan_global * globals __unused,size_t size __unused)281 void __asan_unregister_globals(struct asan_global *globals __unused,
282 size_t size __unused)
283 {
284 }
285