xref: /optee_os/core/kernel/asan.c (revision 7749dda24cf2b1f0a04d1de529cde03b6ca79867)
11bb92983SJerome Forissier // SPDX-License-Identifier: BSD-2-Clause
21d171f95SJens Wiklander /*
31d171f95SJens Wiklander  * Copyright (c) 2016, Linaro Limited
41d171f95SJens Wiklander  */
51d171f95SJens Wiklander 
61d171f95SJens Wiklander #include <assert.h>
71d171f95SJens Wiklander #include <compiler.h>
80b1d6bacSJens Wiklander #include <keep.h>
91d171f95SJens Wiklander #include <kernel/asan.h>
100b1d6bacSJens Wiklander #include <kernel/panic.h>
11c9c847d5SAleksandr Iashchenko #include <printk.h>
12*7749dda2SAleksandr Iashchenko #include <setjmp.h>
131d171f95SJens Wiklander #include <string.h>
140b1d6bacSJens Wiklander #include <trace.h>
151d171f95SJens Wiklander #include <types_ext.h>
161d171f95SJens Wiklander #include <util.h>
171d171f95SJens Wiklander 
1857cf66e1SJens Wiklander #if __GCC_VERSION >= 70000
1957cf66e1SJens Wiklander #define ASAN_ABI_VERSION 7
2057cf66e1SJens Wiklander #else
2157cf66e1SJens Wiklander #define ASAN_ABI_VERSION 6
2257cf66e1SJens Wiklander #endif
2357cf66e1SJens Wiklander 
241d171f95SJens Wiklander struct asan_source_location {
251d171f95SJens Wiklander 	const char *file_name;
261d171f95SJens Wiklander 	int line_no;
271d171f95SJens Wiklander 	int column_no;
281d171f95SJens Wiklander };
291d171f95SJens Wiklander 
301d171f95SJens Wiklander struct asan_global {
311d171f95SJens Wiklander 	uintptr_t beg;
321d171f95SJens Wiklander 	uintptr_t size;
331d171f95SJens Wiklander 	uintptr_t size_with_redzone;
341d171f95SJens Wiklander 	const char *name;
351d171f95SJens Wiklander 	const char *module_name;
361d171f95SJens Wiklander 	uintptr_t has_dynamic_init;
371d171f95SJens Wiklander 	struct asan_source_location *location;
3857cf66e1SJens Wiklander #if ASAN_ABI_VERSION >= 7
3957cf66e1SJens Wiklander 	uintptr_t odr_indicator;
4057cf66e1SJens Wiklander #endif
411d171f95SJens Wiklander };
421d171f95SJens Wiklander 
431d171f95SJens Wiklander static vaddr_t asan_va_base;
441d171f95SJens Wiklander static size_t asan_va_size;
451d171f95SJens Wiklander static bool asan_active;
46c9c847d5SAleksandr Iashchenko static asan_panic_cb_t asan_panic_cb = asan_panic;
471d171f95SJens Wiklander 
48bce4951cSJens Wiklander static int8_t *va_to_shadow(const void *va)
491d171f95SJens Wiklander {
501d171f95SJens Wiklander 	vaddr_t sa = ((vaddr_t)va / ASAN_BLOCK_SIZE) + CFG_ASAN_SHADOW_OFFSET;
511d171f95SJens Wiklander 
521d171f95SJens Wiklander 	return (int8_t *)sa;
531d171f95SJens Wiklander }
541d171f95SJens Wiklander 
55bce4951cSJens Wiklander static size_t va_range_to_shadow_size(const void *begin, const void *end)
561d171f95SJens Wiklander {
571d171f95SJens Wiklander 	return ((vaddr_t)end - (vaddr_t)begin) / ASAN_BLOCK_SIZE;
581d171f95SJens Wiklander }
591d171f95SJens Wiklander 
60bce4951cSJens Wiklander static bool va_range_inside_shadow(const void *begin, const void *end)
611d171f95SJens Wiklander {
621d171f95SJens Wiklander 	vaddr_t b = (vaddr_t)begin;
631d171f95SJens Wiklander 	vaddr_t e = (vaddr_t)end;
641d171f95SJens Wiklander 
651d171f95SJens Wiklander 	if (b >= e)
661d171f95SJens Wiklander 		return false;
671d171f95SJens Wiklander 	return (b >= asan_va_base) && (e <= (asan_va_base + asan_va_size));
681d171f95SJens Wiklander }
691d171f95SJens Wiklander 
70bce4951cSJens Wiklander static bool va_range_outside_shadow(const void *begin, const void *end)
711d171f95SJens Wiklander {
721d171f95SJens Wiklander 	vaddr_t b = (vaddr_t)begin;
731d171f95SJens Wiklander 	vaddr_t e = (vaddr_t)end;
741d171f95SJens Wiklander 
751d171f95SJens Wiklander 	if (b >= e)
761d171f95SJens Wiklander 		return false;
771d171f95SJens Wiklander 	return (e <= asan_va_base) || (b >= (asan_va_base + asan_va_size));
781d171f95SJens Wiklander }
791d171f95SJens Wiklander 
80bce4951cSJens Wiklander static size_t va_misalignment(const void *va)
811d171f95SJens Wiklander {
821d171f95SJens Wiklander 	return (vaddr_t)va & ASAN_BLOCK_MASK;
831d171f95SJens Wiklander }
841d171f95SJens Wiklander 
85bce4951cSJens Wiklander static bool va_is_well_aligned(const void *va)
861d171f95SJens Wiklander {
871d171f95SJens Wiklander 	return !va_misalignment(va);
881d171f95SJens Wiklander }
891d171f95SJens Wiklander 
90bce4951cSJens Wiklander void asan_set_shadowed(const void *begin, const void *end)
911d171f95SJens Wiklander {
921d171f95SJens Wiklander 	vaddr_t b = (vaddr_t)begin;
931d171f95SJens Wiklander 	vaddr_t e = (vaddr_t)end;
941d171f95SJens Wiklander 
951d171f95SJens Wiklander 	assert(!asan_va_base);
961d171f95SJens Wiklander 	assert(va_is_well_aligned(begin));
971d171f95SJens Wiklander 	assert(va_is_well_aligned(end));
981d171f95SJens Wiklander 	assert(b < e);
991d171f95SJens Wiklander 
1001d171f95SJens Wiklander 	asan_va_base = b;
1011d171f95SJens Wiklander 	asan_va_size = e - b;
1021d171f95SJens Wiklander }
1031d171f95SJens Wiklander 
104bce4951cSJens Wiklander void asan_tag_no_access(const void *begin, const void *end)
1051d171f95SJens Wiklander {
1061d171f95SJens Wiklander 	assert(va_is_well_aligned(begin));
1071d171f95SJens Wiklander 	assert(va_is_well_aligned(end));
1081d171f95SJens Wiklander 	assert(va_range_inside_shadow(begin, end));
1091d171f95SJens Wiklander 
110abccd909SJens Wiklander 	asan_memset_unchecked(va_to_shadow(begin), ASAN_DATA_RED_ZONE,
1111d171f95SJens Wiklander 			      va_range_to_shadow_size(begin, end));
1121d171f95SJens Wiklander }
1131d171f95SJens Wiklander 
114bce4951cSJens Wiklander void asan_tag_access(const void *begin, const void *end)
1151d171f95SJens Wiklander {
11624fe8015SJens Wiklander 	if (!asan_va_base || (begin == end))
1171d171f95SJens Wiklander 		return;
1181d171f95SJens Wiklander 
1191d171f95SJens Wiklander 	assert(va_range_inside_shadow(begin, end));
1201d171f95SJens Wiklander 	assert(va_is_well_aligned(begin));
1211d171f95SJens Wiklander 
122abccd909SJens Wiklander 	asan_memset_unchecked(va_to_shadow(begin), 0,
123abccd909SJens Wiklander 			      va_range_to_shadow_size(begin, end));
1241d171f95SJens Wiklander 	if (!va_is_well_aligned(end))
1251d171f95SJens Wiklander 		*va_to_shadow(end) = ASAN_BLOCK_SIZE - va_misalignment(end);
1261d171f95SJens Wiklander }
1271d171f95SJens Wiklander 
128bce4951cSJens Wiklander void asan_tag_heap_free(const void *begin, const void *end)
1291d171f95SJens Wiklander {
1301d171f95SJens Wiklander 	if (!asan_va_base)
1311d171f95SJens Wiklander 		return;
1321d171f95SJens Wiklander 
1331d171f95SJens Wiklander 	assert(va_range_inside_shadow(begin, end));
1341d171f95SJens Wiklander 	assert(va_is_well_aligned(begin));
1351d171f95SJens Wiklander 	assert(va_is_well_aligned(end));
1361d171f95SJens Wiklander 
137abccd909SJens Wiklander 	asan_memset_unchecked(va_to_shadow(begin), ASAN_HEAP_RED_ZONE,
1381d171f95SJens Wiklander 			      va_range_to_shadow_size(begin, end));
1391d171f95SJens Wiklander }
1401d171f95SJens Wiklander 
1415bea6aedSJerome Forissier __inhibit_loop_to_libcall void *asan_memset_unchecked(void *s, int c, size_t n)
142abccd909SJens Wiklander {
143abccd909SJens Wiklander 	uint8_t *b = s;
144abccd909SJens Wiklander 	size_t m;
145abccd909SJens Wiklander 
146abccd909SJens Wiklander 	for (m = 0; m < n; m++)
147abccd909SJens Wiklander 		b[m] = c;
148abccd909SJens Wiklander 
149abccd909SJens Wiklander 	return s;
150abccd909SJens Wiklander }
151abccd909SJens Wiklander 
1525bea6aedSJerome Forissier __inhibit_loop_to_libcall
15306fe4216SJens Wiklander void *asan_memcpy_unchecked(void *__restrict dst, const void *__restrict src,
15406fe4216SJens Wiklander 			    size_t len)
15506fe4216SJens Wiklander {
15606fe4216SJens Wiklander 	uint8_t *__restrict d = dst;
15706fe4216SJens Wiklander 	const uint8_t *__restrict s = src;
15806fe4216SJens Wiklander 	size_t n;
15906fe4216SJens Wiklander 
16006fe4216SJens Wiklander 	for (n = 0; n < len; n++)
16106fe4216SJens Wiklander 		d[n] = s[n];
16206fe4216SJens Wiklander 
16306fe4216SJens Wiklander 	return dst;
16406fe4216SJens Wiklander }
16506fe4216SJens Wiklander 
1661d171f95SJens Wiklander void asan_start(void)
1671d171f95SJens Wiklander {
1681d171f95SJens Wiklander 	assert(asan_va_base && !asan_active);
1691d171f95SJens Wiklander 	asan_active = true;
1701d171f95SJens Wiklander }
1711d171f95SJens Wiklander 
172c9c847d5SAleksandr Iashchenko void __noreturn asan_panic(void)
173c9c847d5SAleksandr Iashchenko {
174c9c847d5SAleksandr Iashchenko 	panic();
175c9c847d5SAleksandr Iashchenko }
176c9c847d5SAleksandr Iashchenko 
177c9c847d5SAleksandr Iashchenko void asan_set_panic_cb(asan_panic_cb_t panic_cb)
178c9c847d5SAleksandr Iashchenko {
179c9c847d5SAleksandr Iashchenko 	asan_panic_cb = panic_cb;
180c9c847d5SAleksandr Iashchenko }
181c9c847d5SAleksandr Iashchenko 
182c9c847d5SAleksandr Iashchenko static void asan_report(vaddr_t addr, size_t size)
183c9c847d5SAleksandr Iashchenko {
184c9c847d5SAleksandr Iashchenko #ifdef KASAN_DUMP_SHADOW
185c9c847d5SAleksandr Iashchenko 	char buf[128] = {0};
186c9c847d5SAleksandr Iashchenko 	int r = 0, rc = 0;
187c9c847d5SAleksandr Iashchenko 	vaddr_t b = 0, e = 0, saddr = 0;
188c9c847d5SAleksandr Iashchenko 
189c9c847d5SAleksandr Iashchenko 	b = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) - ASAN_BLOCK_SIZE;
190c9c847d5SAleksandr Iashchenko 	e = ROUNDDOWN(addr, ASAN_BLOCK_SIZE) + ASAN_BLOCK_SIZE;
191c9c847d5SAleksandr Iashchenko 
192c9c847d5SAleksandr Iashchenko 	/* Print shadow map nearby */
193c9c847d5SAleksandr Iashchenko 	if (va_range_inside_shadow((void *)b, (void *)e)) {
194c9c847d5SAleksandr Iashchenko 		rc = snprintk(buf + r, sizeof(buf) - r, "%lx: ", b);
195c9c847d5SAleksandr Iashchenko 		assert(rc > 0);
196c9c847d5SAleksandr Iashchenko 		r += rc;
197c9c847d5SAleksandr Iashchenko 		for (saddr = b; saddr <= e; saddr += ASAN_BLOCK_SIZE) {
198c9c847d5SAleksandr Iashchenko 			int8_t *sbyte = va_to_shadow((void *)saddr);
199c9c847d5SAleksandr Iashchenko 
200c9c847d5SAleksandr Iashchenko 			rc = snprintk(buf + r, sizeof(buf) - r,
201c9c847d5SAleksandr Iashchenko 				      "0x%02x ", (uint8_t)*sbyte);
202c9c847d5SAleksandr Iashchenko 			assert(rc > 0);
203c9c847d5SAleksandr Iashchenko 			r += rc;
204c9c847d5SAleksandr Iashchenko 		}
205c9c847d5SAleksandr Iashchenko 		EMSG("%s", buf);
206c9c847d5SAleksandr Iashchenko 	}
207c9c847d5SAleksandr Iashchenko #endif
208c9c847d5SAleksandr Iashchenko 	EMSG("[ASAN]: access violation, addr: %lx size: %zu\n",
209c9c847d5SAleksandr Iashchenko 	     addr, size);
210c9c847d5SAleksandr Iashchenko 
211c9c847d5SAleksandr Iashchenko 	asan_panic_cb();
212c9c847d5SAleksandr Iashchenko }
213c9c847d5SAleksandr Iashchenko 
2141d171f95SJens Wiklander static void check_access(vaddr_t addr, size_t size)
2151d171f95SJens Wiklander {
2161d171f95SJens Wiklander 	void *begin = (void *)addr;
2171d171f95SJens Wiklander 	void *end = (void *)(addr + size);
2181d171f95SJens Wiklander 	int8_t *a;
2191d171f95SJens Wiklander 	int8_t *e;
2201d171f95SJens Wiklander 
2211d171f95SJens Wiklander 	if (!asan_active || !size)
2221d171f95SJens Wiklander 		return;
2231d171f95SJens Wiklander 	if (va_range_outside_shadow(begin, end))
2241d171f95SJens Wiklander 		return;
2251d171f95SJens Wiklander 	/*
2261d171f95SJens Wiklander 	 * If it isn't outside it has to be completely inside or there's a
2271d171f95SJens Wiklander 	 * problem.
2281d171f95SJens Wiklander 	 */
2291d171f95SJens Wiklander 	if (!va_range_inside_shadow(begin, end))
2301d171f95SJens Wiklander 		panic();
2311d171f95SJens Wiklander 
232726ce13eSJens Wiklander 	e = va_to_shadow((void *)(addr + size - 1));
233726ce13eSJens Wiklander 	for (a = va_to_shadow(begin); a <= e; a++)
2341d171f95SJens Wiklander 		if (*a < 0)
235c9c847d5SAleksandr Iashchenko 			asan_report(addr, size);
2361d171f95SJens Wiklander 
2371d171f95SJens Wiklander 	if (!va_is_well_aligned(end) &&
2381d171f95SJens Wiklander 	    va_misalignment(end) > (size_t)(*e - ASAN_BLOCK_SIZE))
239c9c847d5SAleksandr Iashchenko 		asan_report(addr, size);
2401d171f95SJens Wiklander }
2411d171f95SJens Wiklander 
2421d171f95SJens Wiklander static void check_load(vaddr_t addr, size_t size)
2431d171f95SJens Wiklander {
2441d171f95SJens Wiklander 	check_access(addr, size);
2451d171f95SJens Wiklander }
2461d171f95SJens Wiklander 
2471d171f95SJens Wiklander static void check_store(vaddr_t addr, size_t size)
2481d171f95SJens Wiklander {
2491d171f95SJens Wiklander 	check_access(addr, size);
2501d171f95SJens Wiklander }
2511d171f95SJens Wiklander 
2521d171f95SJens Wiklander static void __noreturn report_load(vaddr_t addr __unused, size_t size __unused)
2531d171f95SJens Wiklander {
2541d171f95SJens Wiklander 	panic();
2551d171f95SJens Wiklander }
2561d171f95SJens Wiklander 
2571d171f95SJens Wiklander static void __noreturn report_store(vaddr_t addr __unused, size_t size __unused)
2581d171f95SJens Wiklander {
2591d171f95SJens Wiklander 	panic();
2601d171f95SJens Wiklander }
2611d171f95SJens Wiklander 
2621d171f95SJens Wiklander 
2631d171f95SJens Wiklander 
2641d171f95SJens Wiklander #define DEFINE_ASAN_FUNC(type, size)				\
2651d171f95SJens Wiklander 	void __asan_##type##size(vaddr_t addr);			\
2661d171f95SJens Wiklander 	void __asan_##type##size(vaddr_t addr)			\
2671d171f95SJens Wiklander 	{ check_##type(addr, size); }				\
2681d171f95SJens Wiklander 	void __asan_##type##size##_noabort(vaddr_t addr);	\
2691d171f95SJens Wiklander 	void __asan_##type##size##_noabort(vaddr_t addr)	\
2701d171f95SJens Wiklander 	{ check_##type(addr, size); }				\
2711d171f95SJens Wiklander 	void __asan_report_##type##size##_noabort(vaddr_t addr);\
2721d171f95SJens Wiklander 	void __noreturn __asan_report_##type##size##_noabort(vaddr_t addr) \
2731d171f95SJens Wiklander 	{ report_##type(addr, size); }
2741d171f95SJens Wiklander 
2751d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 1)
2761d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 2)
2771d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 4)
2781d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 8)
2791d171f95SJens Wiklander DEFINE_ASAN_FUNC(load, 16)
2801d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 1)
2811d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 2)
2821d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 4)
2831d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 8)
2841d171f95SJens Wiklander DEFINE_ASAN_FUNC(store, 16)
2851d171f95SJens Wiklander 
2861d171f95SJens Wiklander void __asan_loadN_noabort(vaddr_t addr, size_t size);
2871d171f95SJens Wiklander void __asan_loadN_noabort(vaddr_t addr, size_t size)
2881d171f95SJens Wiklander {
2891d171f95SJens Wiklander 	check_load(addr, size);
2901d171f95SJens Wiklander }
2911d171f95SJens Wiklander 
2921d171f95SJens Wiklander void __asan_storeN_noabort(vaddr_t addr, size_t size);
2931d171f95SJens Wiklander void __asan_storeN_noabort(vaddr_t addr, size_t size)
2941d171f95SJens Wiklander {
2951d171f95SJens Wiklander 	check_store(addr, size);
2961d171f95SJens Wiklander }
2971d171f95SJens Wiklander 
2981d171f95SJens Wiklander void __asan_report_load_n_noabort(vaddr_t addr, size_t size);
2991d171f95SJens Wiklander void __noreturn __asan_report_load_n_noabort(vaddr_t addr, size_t size)
3001d171f95SJens Wiklander {
3011d171f95SJens Wiklander 	report_load(addr, size);
3021d171f95SJens Wiklander }
3031d171f95SJens Wiklander 
3041d171f95SJens Wiklander void __asan_report_store_n_noabort(vaddr_t addr, size_t size);
3051d171f95SJens Wiklander void __noreturn __asan_report_store_n_noabort(vaddr_t addr, size_t size)
3061d171f95SJens Wiklander {
3071d171f95SJens Wiklander 	report_store(addr, size);
3081d171f95SJens Wiklander }
3091d171f95SJens Wiklander 
3101d171f95SJens Wiklander void __asan_handle_no_return(void);
311ce553c81SJens Wiklander void __asan_handle_no_return(void)
3121d171f95SJens Wiklander {
3131d171f95SJens Wiklander }
3141d171f95SJens Wiklander 
3151d171f95SJens Wiklander void __asan_register_globals(struct asan_global *globals, size_t size);
3161d171f95SJens Wiklander void __asan_register_globals(struct asan_global *globals, size_t size)
3171d171f95SJens Wiklander {
3181d171f95SJens Wiklander 	size_t n;
3191d171f95SJens Wiklander 
3201d171f95SJens Wiklander 	for (n = 0; n < size; n++)
3211d171f95SJens Wiklander 		asan_tag_access((void *)globals[n].beg,
3221d171f95SJens Wiklander 				(void *)(globals[n].beg + globals[n].size));
3231d171f95SJens Wiklander }
3243639b55fSJerome Forissier DECLARE_KEEP_INIT(__asan_register_globals);
3251d171f95SJens Wiklander 
3261d171f95SJens Wiklander void __asan_unregister_globals(struct asan_global *globals, size_t size);
3271d171f95SJens Wiklander void __asan_unregister_globals(struct asan_global *globals __unused,
3281d171f95SJens Wiklander 			       size_t size __unused)
3291d171f95SJens Wiklander {
3301d171f95SJens Wiklander }
331*7749dda2SAleksandr Iashchenko 
332*7749dda2SAleksandr Iashchenko void asan_handle_longjmp(void *old_sp)
333*7749dda2SAleksandr Iashchenko {
334*7749dda2SAleksandr Iashchenko 	void *top = old_sp;
335*7749dda2SAleksandr Iashchenko 	void *bottom = (void *)ROUNDDOWN((vaddr_t)&top,
336*7749dda2SAleksandr Iashchenko 					 ASAN_BLOCK_SIZE);
337*7749dda2SAleksandr Iashchenko 
338*7749dda2SAleksandr Iashchenko 	asan_tag_access(bottom, top);
339*7749dda2SAleksandr Iashchenko }
340