xref: /optee_os/core/kernel/asan.c (revision 06fe42167e0d6adc58d6fcba24ed97e6102dd47b)
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 <kernel/panic.h>
31 #include <kernel/asan.h>
32 #include <string.h>
33 #include <types_ext.h>
34 #include <util.h>
35 #include <trace.h>
36 
37 struct asan_source_location {
38 	const char *file_name;
39 	int line_no;
40 	int column_no;
41 };
42 
43 struct asan_global {
44 	uintptr_t beg;
45 	uintptr_t size;
46 	uintptr_t size_with_redzone;
47 	const char *name;
48 	const char *module_name;
49 	uintptr_t has_dynamic_init;
50 	struct asan_source_location *location;
51 };
52 
53 static vaddr_t asan_va_base;
54 static size_t asan_va_size;
55 static bool asan_active;
56 
57 static int8_t *va_to_shadow(const void *va)
58 {
59 	vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET;
60 
61 	return (int8_t *)sa;
62 }
63 
64 static size_t va_range_to_shadow_size(const void *begin, const void *end)
65 {
66 	return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE;
67 }
68 
69 static bool va_range_inside_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 (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size));
77 }
78 
79 static bool va_range_outside_shadow(const void *begin, const void *end)
80 {
81 	vaddr_t b = (vaddr_t)begin;
82 	vaddr_t e = (vaddr_t)end;
83 
84 	if (b >= e)
85 		return false;
86 	return (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size));
87 }
88 
89 static size_t va_misalignment(const void *va)
90 {
91 	return (vaddr_t)va & ASAN_BLOCK_MASK;
92 }
93 
94 static bool va_is_well_aligned(const void *va)
95 {
96 	return !va_misalignment(va);
97 }
98 
99 void asan_set_shadowed(const void *begin, const void *end)
100 {
101 	vaddr_t b = (vaddr_t)begin;
102 	vaddr_t e = (vaddr_t)end;
103 
104 	assert(!asan_va_base);
105 	assert(va_is_well_aligned(begin));
106 	assert(va_is_well_aligned(end));
107 	assert(b < e);
108 
109 	asan_va_base = b;
110 	asan_va_size = e - b;
111 }
112 
113 void asan_tag_no_access(const void *begin, const void *end)
114 {
115 	assert(va_is_well_aligned(begin));
116 	assert(va_is_well_aligned(end));
117 	assert(va_range_inside_shadow(begin, end));
118 
119 	asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE,
120 			      va_range_to_shadow_size(begin, end));
121 }
122 
123 void asan_tag_access(const void *begin, const void *end)
124 {
125 	if (!asan_va_base)
126 		return;
127 
128 	assert(va_range_inside_shadow(begin, end));
129 	assert(va_is_well_aligned(begin));
130 
131 	asan_memset_unchecked(va_to_shadow(begin), 0,
132 			      va_range_to_shadow_size(begin, end));
133 	if (!va_is_well_aligned(end))
134 		*va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end);
135 }
136 
137 void asan_tag_heap_free(const void *begin, const void *end)
138 {
139 	if (!asan_va_base)
140 		return;
141 
142 	assert(va_range_inside_shadow(begin, end));
143 	assert(va_is_well_aligned(begin));
144 	assert(va_is_well_aligned(end));
145 
146 	asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE,
147 			      va_range_to_shadow_size(begin, end));
148 }
149 
150 void *asan_memset_unchecked(void *s, int c, size_t n)
151 {
152 	uint8_t *b = s;
153 	size_t m;
154 
155 	for (m = 0; m < n; m++)
156 		b[m] = c;
157 
158 	return s;
159 }
160 
161 void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src,
162 			    size_t len)
163 {
164 	uint8_t *__restrict d = dst;
165 	const uint8_t *__restrict s = src;
166 	size_t n;
167 
168 	for (n = 0; n < len; n++)
169 		d[n] = s[n];
170 
171 	return dst;
172 }
173 
174 void asan_start(void)
175 {
176 	assert(asan_va_base && !asan_active);
177 	asan_active = true;
178 }
179 
180 static void check_access(vaddr_t addr, size_t size)
181 {
182 	void *begin = (void *)addr;
183 	void *end = (void *)(addr + size);
184 	int8_t *a;
185 	int8_t *e;
186 
187 	if (!asan_active || !size)
188 		return;
189 	if (va_range_outside_shadow(begin, end))
190 		return;
191 	/*
192 	 * If it isn't outside it has to be completely inside or there's a
193 	 * problem.
194 	 */
195 	if (!va_range_inside_shadow(begin, end))
196 		panic();
197 
198 	e = va_to_shadow((void *)(addr + size - 1));
199 	for (a = va_to_shadow(begin); a <= e; a++)
200 		if (*a < 0)
201 			panic();
202 
203 	if (!va_is_well_aligned(end) &&
204 	    va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE))
205 		panic();
206 }
207 
208 static void check_load(vaddr_t addr, size_t size)
209 {
210 	check_access(addr, size);
211 }
212 
213 static void check_store(vaddr_t addr, size_t size)
214 {
215 	check_access(addr, size);
216 }
217 
218 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
219 {
220 	panic();
221 }
222 
223 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused)
224 {
225 	panic();
226 }
227 
228 
229 
230 #define DEFINE_ASAN_FUNC(type, size)				\
231 	void __asan_##type##size(vaddr_t addr);			\
232 	void __asan_##type##size(vaddr_t addr)			\
233 	{ check_##type(addr, size); }				\
234 	void __asan_##type##size##_noabort(vaddr_t addr);	\
235 	void __asan_##type##size##_noabort(vaddr_t addr)	\
236 	{ check_##type(addr, size); }				\
237 	void __asan_report_##type##size##_noabort(vaddr_t addr);\
238 	void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \
239 	{ report_##type(addr, size); }
240 
241 DEFINE_ASAN_FUNC(load, 1)
242 DEFINE_ASAN_FUNC(load, 2)
243 DEFINE_ASAN_FUNC(load, 4)
244 DEFINE_ASAN_FUNC(load, 8)
245 DEFINE_ASAN_FUNC(load, 16)
246 DEFINE_ASAN_FUNC(store, 1)
247 DEFINE_ASAN_FUNC(store, 2)
248 DEFINE_ASAN_FUNC(store, 4)
249 DEFINE_ASAN_FUNC(store, 8)
250 DEFINE_ASAN_FUNC(store, 16)
251 
252 void __asan_loadN_noabort(vaddr_t addr, size_t size);
253 void __asan_loadN_noabort(vaddr_t addr, size_t size)
254 {
255 	check_load(addr, size);
256 }
257 
258 void __asan_storeN_noabort(vaddr_t addr, size_t size);
259 void __asan_storeN_noabort(vaddr_t addr, size_t size)
260 {
261 	check_store(addr, size);
262 }
263 
264 void __asan_report_load_n_noabort(vaddr_t addr, size_t size);
265 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size)
266 {
267 	report_load(addr, size);
268 }
269 
270 void __asan_report_store_n_noabort(vaddr_t addr, size_t size);
271 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size)
272 {
273 	report_store(addr, size);
274 }
275 
276 void __asan_handle_no_return(void);
277 void __noreturn __asan_handle_no_return(void)
278 {
279 	panic();
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 
292 void __asan_unregister_globals(struct asan_global *globals, size_t size);
293 void __asan_unregister_globals(struct asan_global *globals __unused,
294 			       size_t size __unused)
295 {
296 }
297