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