xref: /aosp_15_r20/external/coreboot/src/lib/asan.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /* SPDX-License-Identifier: GPL-2.0-only */
2 
3 /*
4  * Address sanitizer support.
5  *
6  * Parts of this file are based on mm/kasan
7  * from the Linux kernel 4.19.137.
8  *
9  */
10 
11 #include <console/console.h>
12 #include <symbols.h>
13 #include <assert.h>
14 #include <arch/symbols.h>
15 #include <asan.h>
16 
asan_mem_to_shadow(const void * addr)17 static inline void *asan_mem_to_shadow(const void *addr)
18 {
19 #if ENV_SEPARATE_ROMSTAGE
20 	return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr -
21 		(uintptr_t)&_car_region_start) >> ASAN_SHADOW_SCALE_SHIFT));
22 #elif ENV_RAMSTAGE
23 	return (void *)((uintptr_t)&_asan_shadow + (((uintptr_t)addr -
24 		(uintptr_t)&_data) >> ASAN_SHADOW_SCALE_SHIFT));
25 #endif
26 }
27 
asan_shadow_to_mem(const void * shadow_addr)28 static inline const void *asan_shadow_to_mem(const void *shadow_addr)
29 {
30 #if ENV_SEPARATE_ROMSTAGE
31 	return (void *)((uintptr_t)&_car_region_start + (((uintptr_t)shadow_addr -
32 		(uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT));
33 #elif ENV_RAMSTAGE
34 	return (void *)((uintptr_t)&_data + (((uintptr_t)shadow_addr -
35 		(uintptr_t)&_asan_shadow) << ASAN_SHADOW_SCALE_SHIFT));
36 #endif
37 }
38 
asan_poison_shadow(const void * address,size_t size,u8 value)39 static void asan_poison_shadow(const void *address, size_t size, u8 value)
40 {
41 	void *shadow_start, *shadow_end;
42 
43 	shadow_start = asan_mem_to_shadow(address);
44 	shadow_end = asan_mem_to_shadow(address + size);
45 
46 	__builtin_memset(shadow_start, value, shadow_end - shadow_start);
47 }
48 
asan_unpoison_shadow(const void * address,size_t size)49 void asan_unpoison_shadow(const void *address, size_t size)
50 {
51 	asan_poison_shadow(address, size, 0);
52 
53 	if (size & ASAN_SHADOW_MASK) {
54 		u8 *shadow = (u8 *)asan_mem_to_shadow(address + size);
55 		*shadow = size & ASAN_SHADOW_MASK;
56 	}
57 }
58 
memory_is_poisoned_1(unsigned long addr)59 static __always_inline bool memory_is_poisoned_1(unsigned long addr)
60 {
61 	s8 shadow_value = *(s8 *)asan_mem_to_shadow((void *)addr);
62 
63 	if (unlikely(shadow_value)) {
64 		s8 last_accessible_byte = addr & ASAN_SHADOW_MASK;
65 		return unlikely(last_accessible_byte >= shadow_value);
66 	}
67 
68 	return false;
69 }
70 
memory_is_poisoned_2_4_8(unsigned long addr,unsigned long size)71 static __always_inline bool memory_is_poisoned_2_4_8(unsigned long addr,
72 						unsigned long size)
73 {
74 	u8 *shadow_addr = (u8 *)asan_mem_to_shadow((void *)addr);
75 
76 	if (unlikely(((addr + size - 1) & ASAN_SHADOW_MASK) < size - 1))
77 		return *shadow_addr || memory_is_poisoned_1(addr + size - 1);
78 
79 	return memory_is_poisoned_1(addr + size - 1);
80 }
81 
memory_is_poisoned_16(unsigned long addr)82 static __always_inline bool memory_is_poisoned_16(unsigned long addr)
83 {
84 	u16 *shadow_addr = (u16 *)asan_mem_to_shadow((void *)addr);
85 
86 	if (unlikely(!IS_ALIGNED(addr, ASAN_SHADOW_SCALE_SIZE)))
87 		return *shadow_addr || memory_is_poisoned_1(addr + 15);
88 
89 	return *shadow_addr;
90 }
91 
bytes_is_nonzero(const u8 * start,size_t size)92 static __always_inline unsigned long bytes_is_nonzero(const u8 *start,
93 					size_t size)
94 {
95 	while (size) {
96 		if (unlikely(*start))
97 			return (unsigned long)start;
98 		start++;
99 		size--;
100 	}
101 
102 	return 0;
103 }
104 
memory_is_nonzero(const void * start,const void * end)105 static __always_inline unsigned long memory_is_nonzero(const void *start,
106 						const void *end)
107 {
108 	unsigned int words;
109 	unsigned long ret;
110 	unsigned int prefix = (unsigned long)start % 8;
111 
112 	if (end - start <= 16)
113 		return bytes_is_nonzero(start, end - start);
114 
115 	if (prefix) {
116 		prefix = 8 - prefix;
117 		ret = bytes_is_nonzero(start, prefix);
118 		if (unlikely(ret))
119 			return ret;
120 		start += prefix;
121 	}
122 
123 	words = (end - start) / 8;
124 	while (words) {
125 		if (unlikely(*(u64 *)start))
126 			return bytes_is_nonzero(start, 8);
127 		start += 8;
128 		words--;
129 	}
130 
131 	return bytes_is_nonzero(start, (end - start) % 8);
132 }
133 
memory_is_poisoned_n(unsigned long addr,size_t size)134 static __always_inline bool memory_is_poisoned_n(unsigned long addr,
135 						size_t size)
136 {
137 	unsigned long ret;
138 
139 	ret = memory_is_nonzero(asan_mem_to_shadow((void *)addr),
140 			asan_mem_to_shadow((void *)addr + size - 1) + 1);
141 
142 	if (unlikely(ret)) {
143 		unsigned long last_byte = addr + size - 1;
144 		s8 *last_shadow = (s8 *)asan_mem_to_shadow((void *)last_byte);
145 
146 		if (unlikely(ret != (unsigned long)last_shadow ||
147 			((long)(last_byte & ASAN_SHADOW_MASK) >= *last_shadow)))
148 			return true;
149 	}
150 	return false;
151 }
152 
memory_is_poisoned(unsigned long addr,size_t size)153 static __always_inline bool memory_is_poisoned(unsigned long addr, size_t size)
154 {
155 	if (__builtin_constant_p(size)) {
156 		switch (size) {
157 		case 1:
158 			return memory_is_poisoned_1(addr);
159 		case 2:
160 		case 4:
161 		case 8:
162 			return memory_is_poisoned_2_4_8(addr, size);
163 		case 16:
164 			return memory_is_poisoned_16(addr);
165 		default:
166 			assert(0);
167 		}
168 	}
169 
170 	return memory_is_poisoned_n(addr, size);
171 }
172 
find_first_bad_addr(const void * addr,size_t size)173 static const void *find_first_bad_addr(const void *addr, size_t size)
174 {
175 	u8 shadow_val = *(u8 *)asan_mem_to_shadow(addr);
176 	const void *first_bad_addr = addr;
177 
178 	while (!shadow_val && first_bad_addr < addr + size) {
179 		first_bad_addr += ASAN_SHADOW_SCALE_SIZE;
180 		shadow_val = *(u8 *)asan_mem_to_shadow(first_bad_addr);
181 	}
182 	return first_bad_addr;
183 }
184 
get_shadow_bug_type(const void * addr,size_t size)185 static const char *get_shadow_bug_type(const void *addr, size_t size)
186 {
187 	const char *bug_type = "unknown-crash";
188 	u8 *shadow_addr;
189 	const void *first_bad_addr;
190 
191 	if (addr < asan_shadow_to_mem((void *) &_asan_shadow))
192 		return bug_type;
193 
194 	first_bad_addr = find_first_bad_addr(addr, size);
195 
196 	shadow_addr = (u8 *)asan_mem_to_shadow(first_bad_addr);
197 
198 	if (*shadow_addr > 0 && *shadow_addr <= ASAN_SHADOW_SCALE_SIZE - 1)
199 		shadow_addr++;
200 
201 	switch (*shadow_addr) {
202 	case 0 ... ASAN_SHADOW_SCALE_SIZE - 1:
203 		bug_type = "out-of-bounds";
204 		break;
205 	case ASAN_GLOBAL_REDZONE:
206 		bug_type = "global-out-of-bounds";
207 		break;
208 	case ASAN_STACK_LEFT:
209 	case ASAN_STACK_MID:
210 	case ASAN_STACK_RIGHT:
211 	case ASAN_STACK_PARTIAL:
212 		bug_type = "stack-out-of-bounds";
213 		break;
214 	case ASAN_USE_AFTER_SCOPE:
215 		bug_type = "use-after-scope";
216 		break;
217 	default:
218 		bug_type = "unknown-crash";
219 	}
220 
221 	return bug_type;
222 }
223 
asan_report(unsigned long addr,size_t size,bool is_write,unsigned long ip)224 void asan_report(unsigned long addr, size_t size, bool is_write,
225 	unsigned long ip)
226 {
227 	const char *bug_type = get_shadow_bug_type((void *) addr, size);
228 	printk(BIOS_ERR, "\n");
229 	printk(BIOS_ERR, "ASan: %s in %p\n", bug_type, (void *) ip);
230 	printk(BIOS_ERR, "%s of %zu byte%s at addr %p\n",
231 		is_write ? "Write" : "Read", size, (size > 1 ? "s" : ""),
232 		(void *) addr);
233 	printk(BIOS_ERR, "\n");
234 }
235 
check_memory_region_inline(unsigned long addr,size_t size,bool write,unsigned long ret_ip)236 static __always_inline void check_memory_region_inline(unsigned long addr,
237 						size_t size, bool write,
238 						unsigned long ret_ip)
239 {
240 #if ENV_SEPARATE_ROMSTAGE
241 	if (((uintptr_t)addr < (uintptr_t)&_car_region_start) ||
242 		((uintptr_t)addr > (uintptr_t)&_ebss))
243 		return;
244 #elif ENV_RAMSTAGE
245 	if (((uintptr_t)addr < (uintptr_t)&_data) ||
246 		((uintptr_t)addr > (uintptr_t)&_eheap))
247 		return;
248 #endif
249 	if (unlikely(size == 0))
250 		return;
251 
252 	if (unlikely((void *)addr <
253 		asan_shadow_to_mem((void *) &_asan_shadow))) {
254 		asan_report(addr, size, write, ret_ip);
255 		return;
256 	}
257 
258 	if (likely(!memory_is_poisoned(addr, size)))
259 		return;
260 
261 	asan_report(addr, size, write, ret_ip);
262 }
263 
check_memory_region(unsigned long addr,size_t size,bool write,unsigned long ret_ip)264 void check_memory_region(unsigned long addr, size_t size, bool write,
265 				unsigned long ret_ip)
266 {
267 	check_memory_region_inline(addr, size, write, ret_ip);
268 }
269 
__asan_shadow_offset(uintptr_t addr)270 uintptr_t __asan_shadow_offset(uintptr_t addr)
271 {
272 #if ENV_SEPARATE_ROMSTAGE
273 	return (uintptr_t)&_asan_shadow - (((uintptr_t)&_car_region_start) >>
274 		ASAN_SHADOW_SCALE_SHIFT);
275 #elif ENV_RAMSTAGE
276 	return (uintptr_t)&_asan_shadow - (((uintptr_t)&_data) >>
277 		ASAN_SHADOW_SCALE_SHIFT);
278 #endif
279 }
280 
register_global(struct asan_global * global)281 static void register_global(struct asan_global *global)
282 {
283 	size_t aligned_size = ALIGN_UP(global->size, ASAN_SHADOW_SCALE_SIZE);
284 
285 	asan_unpoison_shadow(global->beg, global->size);
286 
287 	asan_poison_shadow(global->beg + aligned_size,
288 		global->size_with_redzone - aligned_size,
289 		ASAN_GLOBAL_REDZONE);
290 }
291 
__asan_register_globals(struct asan_global * globals,size_t size)292 void __asan_register_globals(struct asan_global *globals, size_t size)
293 {
294 	int i;
295 
296 	for (i = 0; i < size; i++)
297 		register_global(&globals[i]);
298 }
299 
__asan_unregister_globals(struct asan_global * globals,size_t size)300 void __asan_unregister_globals(struct asan_global *globals, size_t size)
301 {
302 }
303 
304 /*
305  * GCC adds constructors invoking __asan_register_globals() and passes
306  * information about global variable (address, size, size with redzone ...)
307  * to it so we could poison variable's redzone.
308  * This function calls those constructors.
309  */
310 #if ENV_RAMSTAGE
asan_ctors(void)311 static void asan_ctors(void)
312 {
313 	extern long __CTOR_LIST__;
314 	typedef void (*func_ptr)(void);
315 	func_ptr *ctor = (func_ptr *) &__CTOR_LIST__;
316 	if (ctor == NULL)
317 		return;
318 
319 	for (; *ctor != (func_ptr) 0; ctor++)
320 		(*ctor)();
321 }
322 #endif
323 
asan_init(void)324 void asan_init(void)
325 {
326 #if ENV_SEPARATE_ROMSTAGE
327 	size_t size = (size_t)&_ebss - (size_t)&_car_region_start;
328 	asan_unpoison_shadow((void *)&_car_region_start, size);
329 #elif ENV_RAMSTAGE
330 	size_t size = (size_t)&_eheap - (size_t)&_data;
331 	asan_unpoison_shadow((void *)&_data, size);
332 	asan_ctors();
333 #endif
334 }
335 
__asan_poison_stack_memory(const void * addr,size_t size)336 void __asan_poison_stack_memory(const void *addr, size_t size)
337 {
338 	asan_poison_shadow(addr, ALIGN_UP(size, ASAN_SHADOW_SCALE_SIZE),
339 			    ASAN_USE_AFTER_SCOPE);
340 }
341 
__asan_unpoison_stack_memory(const void * addr,size_t size)342 void __asan_unpoison_stack_memory(const void *addr, size_t size)
343 {
344 	asan_unpoison_shadow(addr, size);
345 }
346 
347 #define DEFINE_ASAN_LOAD_STORE(size)	\
348 	void __asan_load##size(unsigned long addr)	\
349 	{	\
350 		check_memory_region_inline(addr, size, false, _RET_IP_);\
351 	}	\
352 	void __asan_load##size##_noabort(unsigned long addr)	\
353 	{	\
354 		check_memory_region_inline(addr, size, false, _RET_IP_);\
355 	}	\
356 	void __asan_store##size(unsigned long addr)	\
357 	{	\
358 		check_memory_region_inline(addr, size, true, _RET_IP_);	\
359 	}	\
360 	void __asan_store##size##_noabort(unsigned long addr)	\
361 	{	\
362 		check_memory_region_inline(addr, size, true, _RET_IP_);	\
363 	}
364 
365 DEFINE_ASAN_LOAD_STORE(1);
366 DEFINE_ASAN_LOAD_STORE(2);
367 DEFINE_ASAN_LOAD_STORE(4);
368 DEFINE_ASAN_LOAD_STORE(8);
369 DEFINE_ASAN_LOAD_STORE(16);
370 
__asan_loadN(unsigned long addr,size_t size)371 void __asan_loadN(unsigned long addr, size_t size)
372 {
373 	check_memory_region(addr, size, false, _RET_IP_);
374 }
375 
__asan_storeN(unsigned long addr,size_t size)376 void __asan_storeN(unsigned long addr, size_t size)
377 {
378 	check_memory_region(addr, size, true, _RET_IP_);
379 }
380 
__asan_loadN_noabort(unsigned long addr,size_t size)381 void __asan_loadN_noabort(unsigned long addr, size_t size)
382 {
383 	check_memory_region(addr, size, false, _RET_IP_);
384 }
385 
__asan_storeN_noabort(unsigned long addr,size_t size)386 void __asan_storeN_noabort(unsigned long addr, size_t size)
387 {
388 	check_memory_region(addr, size, true, _RET_IP_);
389 }
390 
__asan_handle_no_return(void)391 void __asan_handle_no_return(void)
392 {
393 }
394 
395 #define DEFINE_ASAN_SET_SHADOW(byte)	\
396 	void __asan_set_shadow_##byte(const void *addr, size_t size)	\
397 	{	\
398 		__builtin_memset((void *)addr, 0x##byte, size);	\
399 	}
400 
401 DEFINE_ASAN_SET_SHADOW(00);
402 DEFINE_ASAN_SET_SHADOW(f1);
403 DEFINE_ASAN_SET_SHADOW(f2);
404 DEFINE_ASAN_SET_SHADOW(f3);
405 DEFINE_ASAN_SET_SHADOW(f5);
406 DEFINE_ASAN_SET_SHADOW(f8);
407 
408 #define DEFINE_ASAN_REPORT_LOAD(size)	\
409 void __asan_report_load##size##_noabort(unsigned long addr)	\
410 {	\
411 	asan_report(addr, size, false, _RET_IP_);	\
412 }
413 
414 #define DEFINE_ASAN_REPORT_STORE(size)	\
415 void __asan_report_store##size##_noabort(unsigned long addr)	\
416 {	\
417 	asan_report(addr, size, true, _RET_IP_);	\
418 }
419 
420 DEFINE_ASAN_REPORT_LOAD(1);
421 DEFINE_ASAN_REPORT_LOAD(2);
422 DEFINE_ASAN_REPORT_LOAD(4);
423 DEFINE_ASAN_REPORT_LOAD(8);
424 DEFINE_ASAN_REPORT_LOAD(16);
425 DEFINE_ASAN_REPORT_STORE(1);
426 DEFINE_ASAN_REPORT_STORE(2);
427 DEFINE_ASAN_REPORT_STORE(4);
428 DEFINE_ASAN_REPORT_STORE(8);
429 DEFINE_ASAN_REPORT_STORE(16);
430 
__asan_report_load_n_noabort(unsigned long addr,size_t size)431 void __asan_report_load_n_noabort(unsigned long addr, size_t size)
432 {
433 	asan_report(addr, size, false, _RET_IP_);
434 }
435 
__asan_report_store_n_noabort(unsigned long addr,size_t size)436 void __asan_report_store_n_noabort(unsigned long addr, size_t size)
437 {
438 	asan_report(addr, size, true, _RET_IP_);
439 }
440