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