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