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