xref: /optee_os/core/kernel/asan.c (revision 983d02116743476904b68d52ca432d0f79c38c43)
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(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(void *begin, void *end)
65 {
66 	return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE;
67 }
68 
69 static bool va_range_inside_shadow(void *begin, 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(void *begin, 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(void *va)
90 {
91 	return (vaddr_t)va & ASAN_BLOCK_MASK;
92 }
93 
94 static bool va_is_well_aligned(void *va)
95 {
96 	return !va_misalignment(va);
97 }
98 
99 void asan_set_shadowed(void *begin, 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(void *begin, 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 	memset(va_to_shadow(begin), ASAN_DATA_RED_ZONE,
120 	       va_range_to_shadow_size(begin, end));
121 }
122 
123 void asan_tag_access(void *begin, 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 	memset(va_to_shadow(begin), 0, va_range_to_shadow_size(begin, end));
132 	if (!va_is_well_aligned(end))
133 		*va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end);
134 }
135 
136 void asan_tag_heap_free(void *begin, void *end)
137 {
138 	if (!asan_va_base)
139 		return;
140 
141 	assert(va_range_inside_shadow(begin, end));
142 	assert(va_is_well_aligned(begin));
143 	assert(va_is_well_aligned(end));
144 
145 	memset(va_to_shadow(begin), ASAN_HEAP_RED_ZONE,
146 	       va_range_to_shadow_size(begin, end));
147 }
148 
149 void asan_start(void)
150 {
151 	assert(asan_va_base && !asan_active);
152 	asan_active = true;
153 }
154 
155 static void check_access(vaddr_t addr, size_t size)
156 {
157 	void *begin = (void *)addr;
158 	void *end = (void *)(addr + size);
159 	int8_t *a;
160 	int8_t *e;
161 
162 	if (!asan_active || !size)
163 		return;
164 	if (va_range_outside_shadow(begin, end))
165 		return;
166 	/*
167 	 * If it isn't outside it has to be completely inside or there's a
168 	 * problem.
169 	 */
170 	if (!va_range_inside_shadow(begin, end))
171 		panic();
172 
173 	e = va_to_shadow(end);
174 	for (a = va_to_shadow(begin); a != e; a++)
175 		if (*a < 0)
176 			panic();
177 
178 	if (!va_is_well_aligned(end) &&
179 	    va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE))
180 		panic();
181 }
182 
183 static void check_load(vaddr_t addr, size_t size)
184 {
185 	check_access(addr, size);
186 }
187 
188 static void check_store(vaddr_t addr, size_t size)
189 {
190 	check_access(addr, size);
191 }
192 
193 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
194 {
195 	panic();
196 }
197 
198 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused)
199 {
200 	panic();
201 }
202 
203 
204 
205 #define DEFINE_ASAN_FUNC(type, size)				\
206 	void __asan_##type##size(vaddr_t addr);			\
207 	void __asan_##type##size(vaddr_t addr)			\
208 	{ check_##type(addr, size); }				\
209 	void __asan_##type##size##_noabort(vaddr_t addr);	\
210 	void __asan_##type##size##_noabort(vaddr_t addr)	\
211 	{ check_##type(addr, size); }				\
212 	void __asan_report_##type##size##_noabort(vaddr_t addr);\
213 	void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \
214 	{ report_##type(addr, size); }
215 
216 DEFINE_ASAN_FUNC(load, 1)
217 DEFINE_ASAN_FUNC(load, 2)
218 DEFINE_ASAN_FUNC(load, 4)
219 DEFINE_ASAN_FUNC(load, 8)
220 DEFINE_ASAN_FUNC(load, 16)
221 DEFINE_ASAN_FUNC(store, 1)
222 DEFINE_ASAN_FUNC(store, 2)
223 DEFINE_ASAN_FUNC(store, 4)
224 DEFINE_ASAN_FUNC(store, 8)
225 DEFINE_ASAN_FUNC(store, 16)
226 
227 void __asan_loadN_noabort(vaddr_t addr, size_t size);
228 void __asan_loadN_noabort(vaddr_t addr, size_t size)
229 {
230 	check_load(addr, size);
231 }
232 
233 void __asan_storeN_noabort(vaddr_t addr, size_t size);
234 void __asan_storeN_noabort(vaddr_t addr, size_t size)
235 {
236 	check_store(addr, size);
237 }
238 
239 void __asan_report_load_n_noabort(vaddr_t addr, size_t size);
240 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size)
241 {
242 	report_load(addr, size);
243 }
244 
245 void __asan_report_store_n_noabort(vaddr_t addr, size_t size);
246 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size)
247 {
248 	report_store(addr, size);
249 }
250 
251 void __asan_handle_no_return(void);
252 void __noreturn __asan_handle_no_return(void)
253 {
254 	panic();
255 }
256 
257 void __asan_register_globals(struct asan_global *globals, size_t size);
258 void __asan_register_globals(struct asan_global *globals, size_t size)
259 {
260 	size_t n;
261 
262 	for (n = 0; n < size; n++)
263 		asan_tag_access((void *)globals[n].beg,
264 				(void *)(globals[n].beg + globals[n].size));
265 }
266 
267 void __asan_unregister_globals(struct asan_global *globals, size_t size);
268 void __asan_unregister_globals(struct asan_global *globals __unused,
269 			       size_t size __unused)
270 {
271 }
272