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