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