xref: /optee_os/core/kernel/asan.c (revision 1bb929836182ecb96d2d9d268daa807c67596396)
1 // SPDX-License-Identifier: BSD-2-Clause
2 /*
3  * Copyright (c) 2016, Linaro Limited
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice,
10  * this list of conditions and the following disclaimer.
11  *
12  * 2. Redistributions in binary form must reproduce the above copyright notice,
13  * this list of conditions and the following disclaimer in the documentation
14  * and/or other materials provided with the distribution.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <assert.h>
30 #include <compiler.h>
31 #include <keep.h>
32 #include <kernel/asan.h>
33 #include <kernel/panic.h>
34 #include <string.h>
35 #include <trace.h>
36 #include <types_ext.h>
37 #include <util.h>
38 
39 struct asan_source_location {
40 	const char *file_name;
41 	int line_no;
42 	int column_no;
43 };
44 
45 struct asan_global {
46 	uintptr_t beg;
47 	uintptr_t size;
48 	uintptr_t size_with_redzone;
49 	const char *name;
50 	const char *module_name;
51 	uintptr_t has_dynamic_init;
52 	struct asan_source_location *location;
53 };
54 
55 static vaddr_t asan_va_base;
56 static size_t asan_va_size;
57 static bool asan_active;
58 
59 static int8_t *va_to_shadow(const void *va)
60 {
61 	vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET;
62 
63 	return (int8_t *)sa;
64 }
65 
66 static size_t va_range_to_shadow_size(const void *begin, const void *end)
67 {
68 	return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE;
69 }
70 
71 static bool va_range_inside_shadow(const void *begin, const void *end)
72 {
73 	vaddr_t b = (vaddr_t)begin;
74 	vaddr_t e = (vaddr_t)end;
75 
76 	if (b >= e)
77 		return false;
78 	return (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size));
79 }
80 
81 static bool va_range_outside_shadow(const void *begin, const void *end)
82 {
83 	vaddr_t b = (vaddr_t)begin;
84 	vaddr_t e = (vaddr_t)end;
85 
86 	if (b >= e)
87 		return false;
88 	return (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size));
89 }
90 
91 static size_t va_misalignment(const void *va)
92 {
93 	return (vaddr_t)va & ASAN_BLOCK_MASK;
94 }
95 
96 static bool va_is_well_aligned(const void *va)
97 {
98 	return !va_misalignment(va);
99 }
100 
101 void asan_set_shadowed(const void *begin, const void *end)
102 {
103 	vaddr_t b = (vaddr_t)begin;
104 	vaddr_t e = (vaddr_t)end;
105 
106 	assert(!asan_va_base);
107 	assert(va_is_well_aligned(begin));
108 	assert(va_is_well_aligned(end));
109 	assert(b < e);
110 
111 	asan_va_base = b;
112 	asan_va_size = e - b;
113 }
114 
115 void asan_tag_no_access(const void *begin, const void *end)
116 {
117 	assert(va_is_well_aligned(begin));
118 	assert(va_is_well_aligned(end));
119 	assert(va_range_inside_shadow(begin, end));
120 
121 	asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE,
122 			      va_range_to_shadow_size(begin, end));
123 }
124 
125 void asan_tag_access(const void *begin, const void *end)
126 {
127 	if (!asan_va_base || (begin == end))
128 		return;
129 
130 	assert(va_range_inside_shadow(begin, end));
131 	assert(va_is_well_aligned(begin));
132 
133 	asan_memset_unchecked(va_to_shadow(begin), 0,
134 			      va_range_to_shadow_size(begin, end));
135 	if (!va_is_well_aligned(end))
136 		*va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end);
137 }
138 
139 void asan_tag_heap_free(const void *begin, const void *end)
140 {
141 	if (!asan_va_base)
142 		return;
143 
144 	assert(va_range_inside_shadow(begin, end));
145 	assert(va_is_well_aligned(begin));
146 	assert(va_is_well_aligned(end));
147 
148 	asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE,
149 			      va_range_to_shadow_size(begin, end));
150 }
151 
152 void *asan_memset_unchecked(void *s, int c, size_t n)
153 {
154 	uint8_t *b = s;
155 	size_t m;
156 
157 	for (m = 0; m < n; m++)
158 		b[m] = c;
159 
160 	return s;
161 }
162 
163 void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src,
164 			    size_t len)
165 {
166 	uint8_t *__restrict d = dst;
167 	const uint8_t *__restrict s = src;
168 	size_t n;
169 
170 	for (n = 0; n < len; n++)
171 		d[n] = s[n];
172 
173 	return dst;
174 }
175 
176 void asan_start(void)
177 {
178 	assert(asan_va_base && !asan_active);
179 	asan_active = true;
180 }
181 
182 static void check_access(vaddr_t addr, size_t size)
183 {
184 	void *begin = (void *)addr;
185 	void *end = (void *)(addr + size);
186 	int8_t *a;
187 	int8_t *e;
188 
189 	if (!asan_active || !size)
190 		return;
191 	if (va_range_outside_shadow(begin, end))
192 		return;
193 	/*
194 	 * If it isn't outside it has to be completely inside or there's a
195 	 * problem.
196 	 */
197 	if (!va_range_inside_shadow(begin, end))
198 		panic();
199 
200 	e = va_to_shadow((void *)(addr + size - 1));
201 	for (a = va_to_shadow(begin); a <= e; a++)
202 		if (*a < 0)
203 			panic();
204 
205 	if (!va_is_well_aligned(end) &&
206 	    va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE))
207 		panic();
208 }
209 
210 static void check_load(vaddr_t addr, size_t size)
211 {
212 	check_access(addr, size);
213 }
214 
215 static void check_store(vaddr_t addr, size_t size)
216 {
217 	check_access(addr, size);
218 }
219 
220 static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
221 {
222 	panic();
223 }
224 
225 static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused)
226 {
227 	panic();
228 }
229 
230 
231 
232 #define DEFINE_ASAN_FUNC(type, size)				\
233 	void __asan_##type##size(vaddr_t addr);			\
234 	void __asan_##type##size(vaddr_t addr)			\
235 	{ check_##type(addr, size); }				\
236 	void __asan_##type##size##_noabort(vaddr_t addr);	\
237 	void __asan_##type##size##_noabort(vaddr_t addr)	\
238 	{ check_##type(addr, size); }				\
239 	void __asan_report_##type##size##_noabort(vaddr_t addr);\
240 	void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \
241 	{ report_##type(addr, size); }
242 
243 DEFINE_ASAN_FUNC(load, 1)
244 DEFINE_ASAN_FUNC(load, 2)
245 DEFINE_ASAN_FUNC(load, 4)
246 DEFINE_ASAN_FUNC(load, 8)
247 DEFINE_ASAN_FUNC(load, 16)
248 DEFINE_ASAN_FUNC(store, 1)
249 DEFINE_ASAN_FUNC(store, 2)
250 DEFINE_ASAN_FUNC(store, 4)
251 DEFINE_ASAN_FUNC(store, 8)
252 DEFINE_ASAN_FUNC(store, 16)
253 
254 void __asan_loadN_noabort(vaddr_t addr, size_t size);
255 void __asan_loadN_noabort(vaddr_t addr, size_t size)
256 {
257 	check_load(addr, size);
258 }
259 
260 void __asan_storeN_noabort(vaddr_t addr, size_t size);
261 void __asan_storeN_noabort(vaddr_t addr, size_t size)
262 {
263 	check_store(addr, size);
264 }
265 
266 void __asan_report_load_n_noabort(vaddr_t addr, size_t size);
267 void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size)
268 {
269 	report_load(addr, size);
270 }
271 
272 void __asan_report_store_n_noabort(vaddr_t addr, size_t size);
273 void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size)
274 {
275 	report_store(addr, size);
276 }
277 
278 void __asan_handle_no_return(void);
279 void __asan_handle_no_return(void)
280 {
281 }
282 
283 void __asan_register_globals(struct asan_global *globals, size_t size);
284 void __asan_register_globals(struct asan_global *globals, size_t size)
285 {
286 	size_t n;
287 
288 	for (n = 0; n < size; n++)
289 		asan_tag_access((void *)globals[n].beg,
290 				(void *)(globals[n].beg + globals[n].size));
291 }
292 KEEP_INIT(__asan_register_globals);
293 
294 void __asan_unregister_globals(struct asan_global *globals, size_t size);
295 void __asan_unregister_globals(struct asan_global *globals __unused,
296 			       size_t size __unused)
297 {
298 }
299