xref: /optee_os/core/kernel/asan.c (revision 7749dda24cf2b1f0a04d1de529cde03b6ca79867)
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 <printk.h>
12 #include <setjmp.h>
13 #include <string.h>
14 #include <trace.h>
15 #include <types_ext.h>
16 #include <util.h>
17 
18 #if __GCC_VERSION >= 70000
19 #define ASAN_ABI_VERSION 7
20 #else
21 #define ASAN_ABI_VERSION 6
22 #endif
23 
24 struct asan_source_location {
25 	const char *file_name;
26 	int line_no;
27 	int column_no;
28 };
29 
30 struct asan_global {
31 	uintptr_t beg;
32 	uintptr_t size;
33 	uintptr_t size_with_redzone;
34 	const char *name;
35 	const char *module_name;
36 	uintptr_t has_dynamic_init;
37 	struct asan_source_location *location;
38 #if ASAN_ABI_VERSION >= 7
39 	uintptr_t odr_indicator;
40 #endif
41 };
42 
43 static vaddr_t asan_va_base;
44 static size_t asan_va_size;
45 static bool asan_active;
46 static asan_panic_cb_t asan_panic_cb = asan_panic;
47 
48 static int8_t *va_to_shadow(const void *va)
49 {
50 	vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET;
51 
52 	return (int8_t *)sa;
53 }
54 
55 static size_t va_range_to_shadow_size(const void *begin, const void *end)
56 {
57 	return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE;
58 }
59 
60 static bool va_range_inside_shadow(const void *begin, const void *end)
61 {
62 	vaddr_t b = (vaddr_t)begin;
63 	vaddr_t e = (vaddr_t)end;
64 
65 	if (b >= e)
66 		return false;
67 	return (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size));
68 }
69 
70 static bool va_range_outside_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 (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size));
78 }
79 
80 static size_t va_misalignment(const void *va)
81 {
82 	return (vaddr_t)va & ASAN_BLOCK_MASK;
83 }
84 
85 static bool va_is_well_aligned(const void *va)
86 {
87 	return !va_misalignment(va);
88 }
89 
90 void asan_set_shadowed(const void *begin, const void *end)
91 {
92 	vaddr_t b = (vaddr_t)begin;
93 	vaddr_t e = (vaddr_t)end;
94 
95 	assert(!asan_va_base);
96 	assert(va_is_well_aligned(begin));
97 	assert(va_is_well_aligned(end));
98 	assert(b < e);
99 
100 	asan_va_base = b;
101 	asan_va_size = e - b;
102 }
103 
104 void asan_tag_no_access(const void *begin, const void *end)
105 {
106 	assert(va_is_well_aligned(begin));
107 	assert(va_is_well_aligned(end));
108 	assert(va_range_inside_shadow(begin, end));
109 
110 	asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE,
111 			      va_range_to_shadow_size(begin, end));
112 }
113 
114 void asan_tag_access(const void *begin, const void *end)
115 {
116 	if (!asan_va_base || (begin == end))
117 		return;
118 
119 	assert(va_range_inside_shadow(begin, end));
120 	assert(va_is_well_aligned(begin));
121 
122 	asan_memset_unchecked(va_to_shadow(begin), 0,
123 			      va_range_to_shadow_size(begin, end));
124 	if (!va_is_well_aligned(end))
125 		*va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end);
126 }
127 
128 void asan_tag_heap_free(const void *begin, const void *end)
129 {
130 	if (!asan_va_base)
131 		return;
132 
133 	assert(va_range_inside_shadow(begin, end));
134 	assert(va_is_well_aligned(begin));
135 	assert(va_is_well_aligned(end));
136 
137 	asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE,
138 			      va_range_to_shadow_size(begin, end));
139 }
140 
141 __inhibit_loop_to_libcall void *asan_memset_unchecked(void *s, int c, size_t n)
142 {
143 	uint8_t *b = s;
144 	size_t m;
145 
146 	for (m = 0; m < n; m++)
147 		b[m] = c;
148 
149 	return s;
150 }
151 
152 __inhibit_loop_to_libcall
153 void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src,
154 			    size_t len)
155 {
156 	uint8_t *__restrict d = dst;
157 	const uint8_t *__restrict s = src;
158 	size_t n;
159 
160 	for (n = 0; n < len; n++)
161 		d[n] = s[n];
162 
163 	return dst;
164 }
165 
166 void asan_start(void)
167 {
168 	assert(asan_va_base && !asan_active);
169 	asan_active = true;
170 }
171 
172 void __noreturn asan_panic(void)
173 {
174 	panic();
175 }
176 
177 void asan_set_panic_cb(asan_panic_cb_t panic_cb)
178 {
179 	asan_panic_cb = panic_cb;
180 }
181 
182 static void asan_report(vaddr_t addr, size_t size)
183 {
184 #ifdef KASAN_DUMP_SHADOW
185 	char buf[128] = {0};
186 	int r = 0, rc = 0;
187 	vaddr_t b = 0, e = 0, saddr = 0;
188 
189 	b = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) - ASAN_BLOCK_SIZE;
190 	e = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) + ASAN_BLOCK_SIZE;
191 
192 	/* Print shadow map nearby */
193 	if (va_range_inside_shadow((void *)b, (void *)e)) {
194 		rc = snprintk(buf + r, sizeof(buf) - r, "%lx: ", b);
195 		assert(rc > 0);
196 		r += rc;
197 		for (saddr = b; saddr <= e; saddr += ASAN_BLOCK_SIZE) {
198 			int8_t *sbyte = va_to_shadow((void *)saddr);
199 
200 			rc = snprintk(buf + r, sizeof(buf) - r,
201 				      "0x%02x ", (uint8_t)*sbyte);
202 			assert(rc > 0);
203 			r += rc;
204 		}
205 		EMSG("%s", buf);
206 	}
207 #endif
208 	EMSG("[ASAN]: access violation, addr: %lx size: %zu\n",
209 	     addr, size);
210 
211 	asan_panic_cb();
212 }
213 
214 static void check_access(vaddr_t addr, size_t size)
215 {
216 	void *begin = (void *)addr;
217 	void *end = (void *)(addr + size);
218 	int8_t *a;
219 	int8_t *e;
220 
221 	if (!asan_active || !size)
222 		return;
223 	if (va_range_outside_shadow(begin, end))
224 		return;
225 	/*
226 	 * If it isn't outside it has to be completely inside or there's a
227 	 * problem.
228 	 */
229 	if (!va_range_inside_shadow(begin, end))
230 		panic();
231 
232 	e = va_to_shadow((void *)(addr + size - 1));
233 	for (a = va_to_shadow(begin); a <= e; a++)
234 		if (*a < 0)
235 			asan_report(addr, size);
236 
237 	if (!va_is_well_aligned(end) &&
238 	    va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE))
239 		asan_report(addr, size);
240 }
241 
242 static void check_load(vaddr_t addr, size_t size)
243 {
244 	check_access(addr, size);
245 }
246 
247 static void check_store(vaddr_t addr, size_t size)
248 {
249 	check_access(addr, size);
250 }
251 
252 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
253 {
254 	panic();
255 }
256 
257 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused)
258 {
259 	panic();
260 }
261 
262 
263 
264 #define DEFINE_ASAN_FUNC(type, size)				\
265 	void __asan_##type##size(vaddr_t addr);			\
266 	void __asan_##type##size(vaddr_t addr)			\
267 	{ check_##type(addr, size); }				\
268 	void __asan_##type##size##_noabort(vaddr_t addr);	\
269 	void __asan_##type##size##_noabort(vaddr_t addr)	\
270 	{ check_##type(addr, size); }				\
271 	void __asan_report_##type##size##_noabort(vaddr_t addr);\
272 	void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \
273 	{ report_##type(addr, size); }
274 
275 DEFINE_ASAN_FUNC(load, 1)
276 DEFINE_ASAN_FUNC(load, 2)
277 DEFINE_ASAN_FUNC(load, 4)
278 DEFINE_ASAN_FUNC(load, 8)
279 DEFINE_ASAN_FUNC(load, 16)
280 DEFINE_ASAN_FUNC(store, 1)
281 DEFINE_ASAN_FUNC(store, 2)
282 DEFINE_ASAN_FUNC(store, 4)
283 DEFINE_ASAN_FUNC(store, 8)
284 DEFINE_ASAN_FUNC(store, 16)
285 
286 void __asan_loadN_noabort(vaddr_t addr, size_t size);
287 void __asan_loadN_noabort(vaddr_t addr, size_t size)
288 {
289 	check_load(addr, size);
290 }
291 
292 void __asan_storeN_noabort(vaddr_t addr, size_t size);
293 void __asan_storeN_noabort(vaddr_t addr, size_t size)
294 {
295 	check_store(addr, size);
296 }
297 
298 void __asan_report_load_n_noabort(vaddr_t addr, size_t size);
299 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size)
300 {
301 	report_load(addr, size);
302 }
303 
304 void __asan_report_store_n_noabort(vaddr_t addr, size_t size);
305 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size)
306 {
307 	report_store(addr, size);
308 }
309 
310 void __asan_handle_no_return(void);
311 void __asan_handle_no_return(void)
312 {
313 }
314 
315 void __asan_register_globals(struct asan_global *globals, size_t size);
316 void __asan_register_globals(struct asan_global *globals, size_t size)
317 {
318 	size_t n;
319 
320 	for (n = 0; n < size; n++)
321 		asan_tag_access((void *)globals[n].beg,
322 				(void *)(globals[n].beg + globals[n].size));
323 }
324 DECLARE_KEEP_INIT(__asan_register_globals);
325 
326 void __asan_unregister_globals(struct asan_global *globals, size_t size);
327 void __asan_unregister_globals(struct asan_global *globals __unused,
328 			       size_t size __unused)
329 {
330 }
331 
332 void asan_handle_longjmp(void *old_sp)
333 {
334 	void *top = old_sp;
335 	void *bottom = (void *)ROUNDDOWN((vaddr_t)&top,
336 					 ASAN_BLOCK_SIZE);
337 
338 	asan_tag_access(bottom, top);
339 }
340