xref: /optee_os/core/kernel/asan.c (revision 4edd96e6d7a7228e907cf498b23e5b5fbdaf39a0)
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 
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 
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 
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 
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 
77 static size_t va_misalignment(const void *va)
78 {
79 	return (vaddr_t)va & ASAN_BLOCK_MASK;
80 }
81 
82 static bool va_is_well_aligned(const void *va)
83 {
84 	return !va_misalignment(va);
85 }
86 
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 
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 
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 
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 
138 __inhibit_loop_to_libcall 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 
149 __inhibit_loop_to_libcall
150 void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src,
151 			    size_t len)
152 {
153 	uint8_t *__restrict d = dst;
154 	const uint8_t *__restrict s = src;
155 	size_t n;
156 
157 	for (n = 0; n < len; n++)
158 		d[n] = s[n];
159 
160 	return dst;
161 }
162 
163 void asan_start(void)
164 {
165 	assert(asan_va_base && !asan_active);
166 	asan_active = true;
167 }
168 
169 static void check_access(vaddr_t addr, size_t size)
170 {
171 	void *begin = (void *)addr;
172 	void *end = (void *)(addr + size);
173 	int8_t *a;
174 	int8_t *e;
175 
176 	if (!asan_active || !size)
177 		return;
178 	if (va_range_outside_shadow(begin, end))
179 		return;
180 	/*
181 	 * If it isn't outside it has to be completely inside or there's a
182 	 * problem.
183 	 */
184 	if (!va_range_inside_shadow(begin, end))
185 		panic();
186 
187 	e = va_to_shadow((void *)(addr + size - 1));
188 	for (a = va_to_shadow(begin); a <= e; a++)
189 		if (*a < 0)
190 			panic();
191 
192 	if (!va_is_well_aligned(end) &&
193 	    va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE))
194 		panic();
195 }
196 
197 static void check_load(vaddr_t addr, size_t size)
198 {
199 	check_access(addr, size);
200 }
201 
202 static void check_store(vaddr_t addr, size_t size)
203 {
204 	check_access(addr, size);
205 }
206 
207 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
208 {
209 	panic();
210 }
211 
212 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused)
213 {
214 	panic();
215 }
216 
217 
218 
219 #define DEFINE_ASAN_FUNC(type, size)				\
220 	void __asan_##type##size(vaddr_t addr);			\
221 	void __asan_##type##size(vaddr_t addr)			\
222 	{ check_##type(addr, size); }				\
223 	void __asan_##type##size##_noabort(vaddr_t addr);	\
224 	void __asan_##type##size##_noabort(vaddr_t addr)	\
225 	{ check_##type(addr, size); }				\
226 	void __asan_report_##type##size##_noabort(vaddr_t addr);\
227 	void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \
228 	{ report_##type(addr, size); }
229 
230 DEFINE_ASAN_FUNC(load, 1)
231 DEFINE_ASAN_FUNC(load, 2)
232 DEFINE_ASAN_FUNC(load, 4)
233 DEFINE_ASAN_FUNC(load, 8)
234 DEFINE_ASAN_FUNC(load, 16)
235 DEFINE_ASAN_FUNC(store, 1)
236 DEFINE_ASAN_FUNC(store, 2)
237 DEFINE_ASAN_FUNC(store, 4)
238 DEFINE_ASAN_FUNC(store, 8)
239 DEFINE_ASAN_FUNC(store, 16)
240 
241 void __asan_loadN_noabort(vaddr_t addr, size_t size);
242 void __asan_loadN_noabort(vaddr_t addr, size_t size)
243 {
244 	check_load(addr, size);
245 }
246 
247 void __asan_storeN_noabort(vaddr_t addr, size_t size);
248 void __asan_storeN_noabort(vaddr_t addr, size_t size)
249 {
250 	check_store(addr, size);
251 }
252 
253 void __asan_report_load_n_noabort(vaddr_t addr, size_t size);
254 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size)
255 {
256 	report_load(addr, size);
257 }
258 
259 void __asan_report_store_n_noabort(vaddr_t addr, size_t size);
260 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size)
261 {
262 	report_store(addr, size);
263 }
264 
265 void __asan_handle_no_return(void);
266 void __asan_handle_no_return(void)
267 {
268 }
269 
270 void __asan_register_globals(struct asan_global *globals, size_t size);
271 void __asan_register_globals(struct asan_global *globals, size_t size)
272 {
273 	size_t n;
274 
275 	for (n = 0; n < size; n++)
276 		asan_tag_access((void *)globals[n].beg,
277 				(void *)(globals[n].beg + globals[n].size));
278 }
279 DECLARE_KEEP_INIT(__asan_register_globals);
280 
281 void __asan_unregister_globals(struct asan_global *globals, size_t size);
282 void __asan_unregister_globals(struct asan_global *globals __unused,
283 			       size_t size __unused)
284 {
285 }
286