xref: /linux/tools/testing/selftests/bpf/libarena/src/asan.bpf.c (revision b9b23fe1761117f4a0109a25d16d337c900437ad)
19ab78691SEmil Tsalapatis // SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
29ab78691SEmil Tsalapatis /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
39ab78691SEmil Tsalapatis #include <vmlinux.h>
49ab78691SEmil Tsalapatis #include <libarena/common.h>
59ab78691SEmil Tsalapatis #include <libarena/asan.h>
69ab78691SEmil Tsalapatis 
79ab78691SEmil Tsalapatis 
89ab78691SEmil Tsalapatis enum {
99ab78691SEmil Tsalapatis 	/*
109ab78691SEmil Tsalapatis 	 * Is the access checked by check_region_inline
119ab78691SEmil Tsalapatis 	 * a read or a write?
129ab78691SEmil Tsalapatis 	 */
139ab78691SEmil Tsalapatis 	ASAN_READ		= 0x0U,
149ab78691SEmil Tsalapatis 	ASAN_WRITE		= 0x1U,
159ab78691SEmil Tsalapatis };
169ab78691SEmil Tsalapatis 
179ab78691SEmil Tsalapatis /*
189ab78691SEmil Tsalapatis  * Address sanitizer (ASAN) for arena-based BPF programs, inspired
199ab78691SEmil Tsalapatis  * by KASAN.
209ab78691SEmil Tsalapatis  *
219ab78691SEmil Tsalapatis  * The API
229ab78691SEmil Tsalapatis  * -------
239ab78691SEmil Tsalapatis  *
249ab78691SEmil Tsalapatis  * The implementation includes two kinds of components: Implementation
259ab78691SEmil Tsalapatis  * of ASAN hooks injected by LLVM into the program, and API calls that
269ab78691SEmil Tsalapatis  * allocators use to mark memory as valid or invalid. The full list is:
279ab78691SEmil Tsalapatis  *
289ab78691SEmil Tsalapatis  * LLVM stubs:
299ab78691SEmil Tsalapatis  *
309ab78691SEmil Tsalapatis  * void __asan_{load, store}<size>(intptr_t addr)
319ab78691SEmil Tsalapatis  *	Checks whether an access is valid. All variations covered
329ab78691SEmil Tsalapatis  *	by check_region_inline().
339ab78691SEmil Tsalapatis  *
349ab78691SEmil Tsalapatis  * void __asan_{store, load}((intptr_t addr, ssize_t size)
359ab78691SEmil Tsalapatis  *
369ab78691SEmil Tsalapatis  * void __asan_report_{load, store}<size>(intptr_t addr)
379ab78691SEmil Tsalapatis  *	Report an access violation for the program. Used when LLVM
389ab78691SEmil Tsalapatis  *	uses direct code generation for shadow map checks.
399ab78691SEmil Tsalapatis  *
409ab78691SEmil Tsalapatis  * void *__asan_memcpy(void *d, const void *s, size_t n)
419ab78691SEmil Tsalapatis  * void *__asan_memmove(void *d, const void *s, size_t n)
429ab78691SEmil Tsalapatis  * void *__asan_memset(void *p, int c, size_t n)
439ab78691SEmil Tsalapatis  *	Hooks for ASAN instrumentation of the LLVM mem* builtins.
449ab78691SEmil Tsalapatis  *	Currently unimplemented just like the builtins themselves.
459ab78691SEmil Tsalapatis  *
469ab78691SEmil Tsalapatis  * API methods:
479ab78691SEmil Tsalapatis  *
489ab78691SEmil Tsalapatis  * asan_init()
499ab78691SEmil Tsalapatis  *	Initialize the ASAN map for the arena.
509ab78691SEmil Tsalapatis  *
519ab78691SEmil Tsalapatis  * asan_poison()
529ab78691SEmil Tsalapatis  *	Mark a region of memory as poisoned. Accessing poisoned memory
539ab78691SEmil Tsalapatis  *	causes asan_report() to fire. Invoked during free().
549ab78691SEmil Tsalapatis  *
559ab78691SEmil Tsalapatis  * asan_unpoison()
569ab78691SEmil Tsalapatis  *	Mark a region as unpoisoned after alloc().
579ab78691SEmil Tsalapatis  *
589ab78691SEmil Tsalapatis  * asan_shadow_set()
599ab78691SEmil Tsalapatis  *	Check a byte's validity directly.
609ab78691SEmil Tsalapatis  *
619ab78691SEmil Tsalapatis  * The Algorithm In Brief
629ab78691SEmil Tsalapatis  * ----------------------
639ab78691SEmil Tsalapatis  * Each group of 8 bytes is mapped to a "granule" in the shadow map. This
649ab78691SEmil Tsalapatis  * granule is the size of the byte and describes which bytes are valid.
659ab78691SEmil Tsalapatis  * Possible values are:
669ab78691SEmil Tsalapatis  *
679ab78691SEmil Tsalapatis  * 0: All bytes are valid. Makes checks in the middle of an allocated region
689ab78691SEmil Tsalapatis  * (most of them) fast.
699ab78691SEmil Tsalapatis  * (0, 7]: How many consecutive bytes are valid, starting from the lowest one.
709ab78691SEmil Tsalapatis  * The tradeoff is that we can't poison individual bytes in the middle of a
719ab78691SEmil Tsalapatis  * valid region.
729ab78691SEmil Tsalapatis  * [0x80, 0xff]: Special poison values, can be used to denote specific error
739ab78691SEmil Tsalapatis  * modes (e.g., recently freed vs uninitialized memory).
749ab78691SEmil Tsalapatis  *
759ab78691SEmil Tsalapatis  * The mapping between a memory location and its shadow is:
769ab78691SEmil Tsalapatis  * shadow_addr = shadow_base + (addr >> 3). We retain the 8:1 data:shadow
779ab78691SEmil Tsalapatis  * ratio of existing ASAN implementations as a compromise between tracking
789ab78691SEmil Tsalapatis  * granularity and space usage/scan overhead.
799ab78691SEmil Tsalapatis  */
809ab78691SEmil Tsalapatis 
819ab78691SEmil Tsalapatis #ifdef BPF_ARENA_ASAN
829ab78691SEmil Tsalapatis 
839ab78691SEmil Tsalapatis #pragma clang attribute push(__attribute__((no_sanitize("address"))), \
849ab78691SEmil Tsalapatis 			     apply_to = function)
859ab78691SEmil Tsalapatis 
869ab78691SEmil Tsalapatis #define SHADOW_ALL_ZEROES ((u64)-1)
879ab78691SEmil Tsalapatis 
889ab78691SEmil Tsalapatis /*
899ab78691SEmil Tsalapatis  * Canary variable for ASAN violations. Set to the offending address.
909ab78691SEmil Tsalapatis  */
919ab78691SEmil Tsalapatis volatile u64 asan_violated = 0;
929ab78691SEmil Tsalapatis 
939ab78691SEmil Tsalapatis /*
949ab78691SEmil Tsalapatis  * Shadow map occupancy map.
959ab78691SEmil Tsalapatis  */
969ab78691SEmil Tsalapatis volatile u64 __asan_shadow_memory_dynamic_address;
979ab78691SEmil Tsalapatis 
989ab78691SEmil Tsalapatis volatile u32 asan_reported = false;
999ab78691SEmil Tsalapatis volatile bool asan_inited = false;
1009ab78691SEmil Tsalapatis 
1019ab78691SEmil Tsalapatis /*
1029ab78691SEmil Tsalapatis  * Set during program load.
1039ab78691SEmil Tsalapatis  */
1049ab78691SEmil Tsalapatis volatile bool asan_report_once = false;
1059ab78691SEmil Tsalapatis 
1069ab78691SEmil Tsalapatis /*
1079ab78691SEmil Tsalapatis  * BPF does not currently support the memset/memcpy/memcmp intrinsics.
1089ab78691SEmil Tsalapatis  * For large sequential copies, or assignments of large data structures,
1099ab78691SEmil Tsalapatis  * the frontend will generate an intrinsic that causes the BPF backend
1109ab78691SEmil Tsalapatis  * to exit due to a missing implementation. Provide a simple implementation
1119ab78691SEmil Tsalapatis  * just for memset to use it for poisoning/unpoisoning the map.
1129ab78691SEmil Tsalapatis  */
113*b9b23fe1SEmil Tsalapatis __weak int asan_memset(s8 __arena *dst, s8 val, size_t size)
1149ab78691SEmil Tsalapatis {
1159ab78691SEmil Tsalapatis 	size_t i;
1169ab78691SEmil Tsalapatis 
1179ab78691SEmil Tsalapatis 	for (i = zero; i < size && can_loop; i++)
1189ab78691SEmil Tsalapatis 		dst[i] = val;
1199ab78691SEmil Tsalapatis 
1209ab78691SEmil Tsalapatis 	return 0;
1219ab78691SEmil Tsalapatis }
1229ab78691SEmil Tsalapatis 
1239ab78691SEmil Tsalapatis /* Validate a 1-byte access, always within a single byte. */
124*b9b23fe1SEmil Tsalapatis static __always_inline bool memory_is_poisoned_1(s8 __arena *addr)
1259ab78691SEmil Tsalapatis {
126*b9b23fe1SEmil Tsalapatis 	s8 shadow_value = *(s8 __arena *)mem_to_shadow(addr);
1279ab78691SEmil Tsalapatis 
1289ab78691SEmil Tsalapatis 	/* Byte is 0, access is valid. */
1299ab78691SEmil Tsalapatis 	if (likely(!shadow_value))
1309ab78691SEmil Tsalapatis 		return false;
1319ab78691SEmil Tsalapatis 
1329ab78691SEmil Tsalapatis 	/*
1339ab78691SEmil Tsalapatis 	 * Byte is non-zero. Access is valid if granule offset in [0, shadow_value),
1349ab78691SEmil Tsalapatis 	 * so the memory is poisoned if shadow_value is negative or smaller than
1359ab78691SEmil Tsalapatis 	 * the granule's value.
1369ab78691SEmil Tsalapatis 	 */
1379ab78691SEmil Tsalapatis 
1389ab78691SEmil Tsalapatis 	return ASAN_GRANULE(addr) >= shadow_value;
1399ab78691SEmil Tsalapatis }
1409ab78691SEmil Tsalapatis 
1419ab78691SEmil Tsalapatis /* Validate a 2- 4-, 8-byte access, shadow spans up to 2 bytes. */
142*b9b23fe1SEmil Tsalapatis static __always_inline bool memory_is_poisoned_2_4_8(s8 __arena *addr, u64 size)
1439ab78691SEmil Tsalapatis {
1449ab78691SEmil Tsalapatis 	u64 end = (u64)addr + size - 1;
1459ab78691SEmil Tsalapatis 
1469ab78691SEmil Tsalapatis 	/*
1479ab78691SEmil Tsalapatis 	 * Region fully within a single byte (addition didn't
1489ab78691SEmil Tsalapatis 	 * overflow above ASAN_GRANULE).
1499ab78691SEmil Tsalapatis 	 */
1509ab78691SEmil Tsalapatis 	if (likely(ASAN_GRANULE(end) >= size - 1))
151*b9b23fe1SEmil Tsalapatis 		return memory_is_poisoned_1((s8 __arena *)end);
1529ab78691SEmil Tsalapatis 
1539ab78691SEmil Tsalapatis 	/*
1549ab78691SEmil Tsalapatis 	 * Otherwise first byte must be fully unpoisoned, and second byte
1559ab78691SEmil Tsalapatis 	 * must be unpoisoned up to the end of the accessed region.
1569ab78691SEmil Tsalapatis 	 */
1579ab78691SEmil Tsalapatis 
158*b9b23fe1SEmil Tsalapatis 	return *(s8 __arena *)mem_to_shadow(addr) || memory_is_poisoned_1((s8 __arena *)end);
1599ab78691SEmil Tsalapatis }
1609ab78691SEmil Tsalapatis 
161*b9b23fe1SEmil Tsalapatis __weak bool asan_shadow_set(void __arena *addr)
1629ab78691SEmil Tsalapatis {
1639ab78691SEmil Tsalapatis 	return memory_is_poisoned_1(addr);
1649ab78691SEmil Tsalapatis }
1659ab78691SEmil Tsalapatis 
1669ab78691SEmil Tsalapatis static __always_inline u64 first_nonzero_byte(u64 addr, size_t size)
1679ab78691SEmil Tsalapatis {
1689ab78691SEmil Tsalapatis 	while (size && can_loop) {
169*b9b23fe1SEmil Tsalapatis 		if (unlikely(*(s8 __arena *)addr))
1709ab78691SEmil Tsalapatis 			return addr;
1719ab78691SEmil Tsalapatis 		addr += 1;
1729ab78691SEmil Tsalapatis 		size -= 1;
1739ab78691SEmil Tsalapatis 	}
1749ab78691SEmil Tsalapatis 
1759ab78691SEmil Tsalapatis 	return SHADOW_ALL_ZEROES;
1769ab78691SEmil Tsalapatis }
1779ab78691SEmil Tsalapatis 
178*b9b23fe1SEmil Tsalapatis static __always_inline bool memory_is_poisoned_n(s8 __arena *addr, u64 size)
1799ab78691SEmil Tsalapatis {
1809ab78691SEmil Tsalapatis 	u64 ret;
1819ab78691SEmil Tsalapatis 	u64 start;
1829ab78691SEmil Tsalapatis 	u64 end;
1839ab78691SEmil Tsalapatis 
1849ab78691SEmil Tsalapatis 	/* Size of [start, end] is end - start + 1. */
1859ab78691SEmil Tsalapatis 	start = (u64)mem_to_shadow(addr);
1869ab78691SEmil Tsalapatis 	end = (u64)mem_to_shadow(addr + size - 1);
1879ab78691SEmil Tsalapatis 
1889ab78691SEmil Tsalapatis 	ret = first_nonzero_byte(start, (end - start) + 1);
1899ab78691SEmil Tsalapatis 	if (likely(ret == SHADOW_ALL_ZEROES))
1909ab78691SEmil Tsalapatis 		return false;
1919ab78691SEmil Tsalapatis 
192*b9b23fe1SEmil Tsalapatis 	return unlikely(ret != end || ASAN_GRANULE(addr + size - 1) >= *(s8 __arena *)end);
1939ab78691SEmil Tsalapatis }
1949ab78691SEmil Tsalapatis 
195*b9b23fe1SEmil Tsalapatis __weak int asan_report(s8 __arena *addr, size_t sz, u32 flags)
1969ab78691SEmil Tsalapatis {
1979ab78691SEmil Tsalapatis 	u32 reported = __sync_val_compare_and_swap(&asan_reported, false, true);
1989ab78691SEmil Tsalapatis 
1999ab78691SEmil Tsalapatis 	/* Only report the first ASAN violation. */
2009ab78691SEmil Tsalapatis 	if (reported && asan_report_once)
2019ab78691SEmil Tsalapatis 		return 0;
2029ab78691SEmil Tsalapatis 
2039ab78691SEmil Tsalapatis 	asan_violated = (u64)addr;
2049ab78691SEmil Tsalapatis 
2059ab78691SEmil Tsalapatis 	arena_stderr("Memory violation for address %p (0x%lx) for %s of size %ld\n",
2069ab78691SEmil Tsalapatis 			addr, (u64)addr,
2079ab78691SEmil Tsalapatis 			(flags & ASAN_WRITE) ? "write" : "read",
2089ab78691SEmil Tsalapatis 			sz);
2099ab78691SEmil Tsalapatis 	bpf_stream_print_stack(BPF_STDERR);
2109ab78691SEmil Tsalapatis 
2119ab78691SEmil Tsalapatis 	return 0;
2129ab78691SEmil Tsalapatis }
2139ab78691SEmil Tsalapatis 
214*b9b23fe1SEmil Tsalapatis static __always_inline bool check_asan_args(s8 __arena *addr, size_t size,
2159ab78691SEmil Tsalapatis 					    bool *result)
2169ab78691SEmil Tsalapatis {
2179ab78691SEmil Tsalapatis 	bool valid = true;
2189ab78691SEmil Tsalapatis 
2199ab78691SEmil Tsalapatis 	/* Size 0 accesses are valid even if the address is invalid. */
2209ab78691SEmil Tsalapatis 	if (unlikely(size == 0))
2219ab78691SEmil Tsalapatis 		goto confirmed_valid;
2229ab78691SEmil Tsalapatis 
2239ab78691SEmil Tsalapatis 	/*
2249ab78691SEmil Tsalapatis 	 * Wraparound is possible for values close to the the edge of the
2259ab78691SEmil Tsalapatis 	 * 4GiB boundary of the arena (last valid address is 1UL << 32 - 1).
2269ab78691SEmil Tsalapatis 	 *
2279ab78691SEmil Tsalapatis 	 *
2289ab78691SEmil Tsalapatis 	 * The wraparound detection below works for small sizes. check_asan_args is
2299ab78691SEmil Tsalapatis 	 * always called from the builtin ASAN checks, so 1 <= size <= 64. Even
2309ab78691SEmil Tsalapatis 	 * for storeN/loadN that we do not expect to encounter the intrinsics will
2319ab78691SEmil Tsalapatis 	 * not have a large enough size that:
2329ab78691SEmil Tsalapatis 	 *
2339ab78691SEmil Tsalapatis 	 * - addr + size  > MAX_U32
2349ab78691SEmil Tsalapatis 	 * - (u32)(addr + size) > (u32) addr
2359ab78691SEmil Tsalapatis 	 *
2369ab78691SEmil Tsalapatis 	 * which would defeat wraparound detection.
2379ab78691SEmil Tsalapatis 	 */
2389ab78691SEmil Tsalapatis 	if (unlikely((u32)(u64)(addr + size) < (u32)(u64)addr))
2399ab78691SEmil Tsalapatis 		goto confirmed_invalid;
2409ab78691SEmil Tsalapatis 
2419ab78691SEmil Tsalapatis 	return false;
2429ab78691SEmil Tsalapatis 
2439ab78691SEmil Tsalapatis confirmed_invalid:
2449ab78691SEmil Tsalapatis 	valid = false;
2459ab78691SEmil Tsalapatis 
2469ab78691SEmil Tsalapatis 	/* FALLTHROUGH */
2479ab78691SEmil Tsalapatis confirmed_valid:
2489ab78691SEmil Tsalapatis 	*result = valid;
2499ab78691SEmil Tsalapatis 
2509ab78691SEmil Tsalapatis 	return true;
2519ab78691SEmil Tsalapatis }
2529ab78691SEmil Tsalapatis 
2539ab78691SEmil Tsalapatis static __always_inline bool check_region_inline(intptr_t ptr, size_t size,
2549ab78691SEmil Tsalapatis 						u32 flags)
2559ab78691SEmil Tsalapatis {
256*b9b23fe1SEmil Tsalapatis 	s8 __arena *addr = (s8 __arena *)(u64)ptr;
2579ab78691SEmil Tsalapatis 	bool is_poisoned, is_valid;
2589ab78691SEmil Tsalapatis 
2599ab78691SEmil Tsalapatis 	if (check_asan_args(addr, size, &is_valid)) {
2609ab78691SEmil Tsalapatis 		if (!is_valid)
2619ab78691SEmil Tsalapatis 			asan_report(addr, size, flags);
2629ab78691SEmil Tsalapatis 		return is_valid;
2639ab78691SEmil Tsalapatis 	}
2649ab78691SEmil Tsalapatis 
2659ab78691SEmil Tsalapatis 	switch (size) {
2669ab78691SEmil Tsalapatis 	case 1:
2679ab78691SEmil Tsalapatis 		is_poisoned = memory_is_poisoned_1(addr);
2689ab78691SEmil Tsalapatis 		break;
2699ab78691SEmil Tsalapatis 	case 2:
2709ab78691SEmil Tsalapatis 	case 4:
2719ab78691SEmil Tsalapatis 	case 8:
2729ab78691SEmil Tsalapatis 		is_poisoned = memory_is_poisoned_2_4_8(addr, size);
2739ab78691SEmil Tsalapatis 		break;
2749ab78691SEmil Tsalapatis 	default:
2759ab78691SEmil Tsalapatis 		is_poisoned = memory_is_poisoned_n(addr, size);
2769ab78691SEmil Tsalapatis 	}
2779ab78691SEmil Tsalapatis 
2789ab78691SEmil Tsalapatis 	if (is_poisoned) {
2799ab78691SEmil Tsalapatis 		asan_report(addr, size, flags);
2809ab78691SEmil Tsalapatis 		return false;
2819ab78691SEmil Tsalapatis 	}
2829ab78691SEmil Tsalapatis 
2839ab78691SEmil Tsalapatis 	return true;
2849ab78691SEmil Tsalapatis }
2859ab78691SEmil Tsalapatis 
2869ab78691SEmil Tsalapatis /*
2879ab78691SEmil Tsalapatis  * __alias is not supported for BPF so define *__noabort() variants as wrappers.
2889ab78691SEmil Tsalapatis  */
2899ab78691SEmil Tsalapatis #define DEFINE_ASAN_LOAD_STORE(size)                                  \
2909ab78691SEmil Tsalapatis 	__hidden void __asan_store##size(intptr_t addr)                  \
2919ab78691SEmil Tsalapatis 	{                                                             \
2929ab78691SEmil Tsalapatis 		check_region_inline(addr, size, ASAN_WRITE);          \
2939ab78691SEmil Tsalapatis 	}                                                             \
2949ab78691SEmil Tsalapatis 	__hidden void __asan_store##size##_noabort(intptr_t addr)        \
2959ab78691SEmil Tsalapatis 	{                                                             \
2969ab78691SEmil Tsalapatis 		check_region_inline(addr, size, ASAN_WRITE);          \
2979ab78691SEmil Tsalapatis 	}                                                             \
2989ab78691SEmil Tsalapatis 	__hidden void __asan_load##size(intptr_t addr)                   \
2999ab78691SEmil Tsalapatis 	{                                                             \
3009ab78691SEmil Tsalapatis 		check_region_inline(addr, size, ASAN_READ);           \
3019ab78691SEmil Tsalapatis 	}                                                             \
3029ab78691SEmil Tsalapatis 	__hidden void __asan_load##size##_noabort(intptr_t addr)         \
3039ab78691SEmil Tsalapatis 	{                                                             \
3049ab78691SEmil Tsalapatis 		check_region_inline(addr, size, ASAN_READ);           \
3059ab78691SEmil Tsalapatis 	}                                                             \
3069ab78691SEmil Tsalapatis 	__hidden void __asan_report_store##size(intptr_t addr)           \
3079ab78691SEmil Tsalapatis 	{                                                             \
308*b9b23fe1SEmil Tsalapatis 		asan_report((s8 __arena *)addr, size, ASAN_WRITE);           \
3099ab78691SEmil Tsalapatis 	}                                                             \
3109ab78691SEmil Tsalapatis 	__hidden void __asan_report_store##size##_noabort(intptr_t addr) \
3119ab78691SEmil Tsalapatis 	{                                                             \
312*b9b23fe1SEmil Tsalapatis 		asan_report((s8 __arena *)addr, size, ASAN_WRITE);           \
3139ab78691SEmil Tsalapatis 	}                                                             \
3149ab78691SEmil Tsalapatis 	__hidden void __asan_report_load##size(intptr_t addr)            \
3159ab78691SEmil Tsalapatis 	{                                                             \
316*b9b23fe1SEmil Tsalapatis 		asan_report((s8 __arena *)addr, size, ASAN_READ);            \
3179ab78691SEmil Tsalapatis 	}                                                             \
3189ab78691SEmil Tsalapatis 	__hidden void __asan_report_load##size##_noabort(intptr_t addr)  \
3199ab78691SEmil Tsalapatis 	{                                                             \
320*b9b23fe1SEmil Tsalapatis 		asan_report((s8 __arena *)addr, size, ASAN_READ);            \
3219ab78691SEmil Tsalapatis 	}
3229ab78691SEmil Tsalapatis 
3239ab78691SEmil Tsalapatis DEFINE_ASAN_LOAD_STORE(1);
3249ab78691SEmil Tsalapatis DEFINE_ASAN_LOAD_STORE(2);
3259ab78691SEmil Tsalapatis DEFINE_ASAN_LOAD_STORE(4);
3269ab78691SEmil Tsalapatis DEFINE_ASAN_LOAD_STORE(8);
3279ab78691SEmil Tsalapatis 
3289ab78691SEmil Tsalapatis void __asan_storeN(intptr_t addr, ssize_t size)
3299ab78691SEmil Tsalapatis {
3309ab78691SEmil Tsalapatis 	check_region_inline(addr, size, ASAN_WRITE);
3319ab78691SEmil Tsalapatis }
3329ab78691SEmil Tsalapatis 
3339ab78691SEmil Tsalapatis void __asan_storeN_noabort(intptr_t addr, ssize_t size)
3349ab78691SEmil Tsalapatis {
3359ab78691SEmil Tsalapatis 	check_region_inline(addr, size, ASAN_WRITE);
3369ab78691SEmil Tsalapatis }
3379ab78691SEmil Tsalapatis 
3389ab78691SEmil Tsalapatis void __asan_loadN(intptr_t addr, ssize_t size)
3399ab78691SEmil Tsalapatis {
3409ab78691SEmil Tsalapatis 	check_region_inline(addr, size, ASAN_READ);
3419ab78691SEmil Tsalapatis }
3429ab78691SEmil Tsalapatis 
3439ab78691SEmil Tsalapatis void __asan_loadN_noabort(intptr_t addr, ssize_t size)
3449ab78691SEmil Tsalapatis {
3459ab78691SEmil Tsalapatis 	check_region_inline(addr, size, ASAN_READ);
3469ab78691SEmil Tsalapatis }
3479ab78691SEmil Tsalapatis 
3489ab78691SEmil Tsalapatis /*
3499ab78691SEmil Tsalapatis  * We currently do not sanitize globals.
3509ab78691SEmil Tsalapatis  */
3519ab78691SEmil Tsalapatis void __asan_register_globals(intptr_t globals, size_t n)
3529ab78691SEmil Tsalapatis {
3539ab78691SEmil Tsalapatis }
3549ab78691SEmil Tsalapatis 
3559ab78691SEmil Tsalapatis void __asan_unregister_globals(intptr_t globals, size_t n)
3569ab78691SEmil Tsalapatis {
3579ab78691SEmil Tsalapatis }
3589ab78691SEmil Tsalapatis 
3599ab78691SEmil Tsalapatis /*
3609ab78691SEmil Tsalapatis  * We do not currently have memcpy/memmove/memset intrinsics
3619ab78691SEmil Tsalapatis  * in LLVM. Do not implement sanitization.
3629ab78691SEmil Tsalapatis  */
3639ab78691SEmil Tsalapatis void *__asan_memcpy(void *d, const void *s, size_t n)
3649ab78691SEmil Tsalapatis {
3659ab78691SEmil Tsalapatis 	arena_stderr("ASAN: Unexpected %s call", __func__);
3669ab78691SEmil Tsalapatis 	return NULL;
3679ab78691SEmil Tsalapatis }
3689ab78691SEmil Tsalapatis 
3699ab78691SEmil Tsalapatis void *__asan_memmove(void *d, const void *s, size_t n)
3709ab78691SEmil Tsalapatis {
3719ab78691SEmil Tsalapatis 	arena_stderr("ASAN: Unexpected %s call", __func__);
3729ab78691SEmil Tsalapatis 	return NULL;
3739ab78691SEmil Tsalapatis }
3749ab78691SEmil Tsalapatis 
3759ab78691SEmil Tsalapatis void *__asan_memset(void *p, int c, size_t n)
3769ab78691SEmil Tsalapatis {
3779ab78691SEmil Tsalapatis 	arena_stderr("ASAN: Unexpected %s call", __func__);
3789ab78691SEmil Tsalapatis 	return NULL;
3799ab78691SEmil Tsalapatis }
3809ab78691SEmil Tsalapatis 
3819ab78691SEmil Tsalapatis /*
3829ab78691SEmil Tsalapatis  * Poisoning code, used when we add more freed memory to the allocator by:
3839ab78691SEmil Tsalapatis  * 	a) pulling memory from the arena segment using bpf_arena_alloc_pages()
3849ab78691SEmil Tsalapatis  * 	b) freeing memory from application code
3859ab78691SEmil Tsalapatis  */
3869ab78691SEmil Tsalapatis __hidden __noasan int asan_poison(void __arena *addr, s8 val, size_t size)
3879ab78691SEmil Tsalapatis {
388*b9b23fe1SEmil Tsalapatis 	s8 __arena *shadow;
3899ab78691SEmil Tsalapatis 	size_t len;
3909ab78691SEmil Tsalapatis 
3919ab78691SEmil Tsalapatis 	/*
3929ab78691SEmil Tsalapatis 	 * Poisoning from a non-granule address makes no sense: We can only allocate
3939ab78691SEmil Tsalapatis 	 * memory to the application that has a granule-aligned starting address,
3949ab78691SEmil Tsalapatis 	 * and bpf_arena_alloc_pages returns page-aligned memory. A non-aligned
3959ab78691SEmil Tsalapatis 	 * addr then implies we're freeing a different address than the one we
3969ab78691SEmil Tsalapatis 	 * allocated.
3979ab78691SEmil Tsalapatis 	 */
3989ab78691SEmil Tsalapatis 	if (unlikely((u64)addr & ASAN_GRANULE_MASK))
3999ab78691SEmil Tsalapatis 		return -EINVAL;
4009ab78691SEmil Tsalapatis 
4019ab78691SEmil Tsalapatis 	/*
4029ab78691SEmil Tsalapatis 	 * We cannot free an unaligned region because it'd be possible that we
4039ab78691SEmil Tsalapatis 	 * cannot describe the resulting poisoning state of the granule in
4049ab78691SEmil Tsalapatis 	 * the ASAN encoding.
4059ab78691SEmil Tsalapatis 	 *
4069ab78691SEmil Tsalapatis 	 * Every granule represents a region of memory that looks like the
4079ab78691SEmil Tsalapatis 	 * following (P for poisoned bytes, C for clear):
4089ab78691SEmil Tsalapatis 	 *
4099ab78691SEmil Tsalapatis 	 * <Clear>  <Poisoned>
4109ab78691SEmil Tsalapatis 	 * [ C C C ... P P ]
4119ab78691SEmil Tsalapatis 	 *
4129ab78691SEmil Tsalapatis 	 * The value of the granule's shadow map is the number of clear bytes in
4139ab78691SEmil Tsalapatis 	 * it. We cannot represent granules with the following state:
4149ab78691SEmil Tsalapatis 	 *
4159ab78691SEmil Tsalapatis 	 * [ P P ... C C ... P P ]
4169ab78691SEmil Tsalapatis 	 *
4179ab78691SEmil Tsalapatis 	 * That would be possible if we could free unaligned regions, so prevent that.
4189ab78691SEmil Tsalapatis 	 */
4199ab78691SEmil Tsalapatis 	if (unlikely(size & ASAN_GRANULE_MASK))
4209ab78691SEmil Tsalapatis 		return -EINVAL;
4219ab78691SEmil Tsalapatis 
4229ab78691SEmil Tsalapatis 	shadow = mem_to_shadow(addr);
4239ab78691SEmil Tsalapatis 	len = size >> ASAN_SHADOW_SHIFT;
4249ab78691SEmil Tsalapatis 
4259ab78691SEmil Tsalapatis 	asan_memset(shadow, val, len);
4269ab78691SEmil Tsalapatis 
4279ab78691SEmil Tsalapatis 	return 0;
4289ab78691SEmil Tsalapatis }
4299ab78691SEmil Tsalapatis 
4309ab78691SEmil Tsalapatis /*
4319ab78691SEmil Tsalapatis  * Unpoisoning code for marking memory as valid during allocation calls.
4329ab78691SEmil Tsalapatis  *
4339ab78691SEmil Tsalapatis  * Very similar to asan_poison, except we need to round up instead of
4349ab78691SEmil Tsalapatis  * down, then partially poison the last granule if necessary.
4359ab78691SEmil Tsalapatis  *
4369ab78691SEmil Tsalapatis  * Partial poisoning is useful for keeping the padding poisoned. Allocations
4379ab78691SEmil Tsalapatis  * are granule-aligned, so we we're reserving granule-aligned sizes for the
4389ab78691SEmil Tsalapatis  * allocation. However, we want to still treat accesses to the padding as
4399ab78691SEmil Tsalapatis  * invalid. Partial poisoning takes care of that. Freeing and poisoning the
4409ab78691SEmil Tsalapatis  * memory is still done in granule-aligned sizes and repoisons the already
4419ab78691SEmil Tsalapatis  * poisoned padding.
4429ab78691SEmil Tsalapatis  */
4439ab78691SEmil Tsalapatis __hidden __noasan int asan_unpoison(void __arena *addr, size_t size)
4449ab78691SEmil Tsalapatis {
4459ab78691SEmil Tsalapatis 	size_t partial = size & ASAN_GRANULE_MASK;
446*b9b23fe1SEmil Tsalapatis 	s8 __arena *shadow;
4479ab78691SEmil Tsalapatis 	size_t len;
4489ab78691SEmil Tsalapatis 
4499ab78691SEmil Tsalapatis 	/*
4509ab78691SEmil Tsalapatis 	 * We cannot allocate in the middle of the granule. The ASAN shadow
4519ab78691SEmil Tsalapatis 	 * map encoding only describes regions of memory where every granule
4529ab78691SEmil Tsalapatis 	 * follows this format (P for poisoned, C for clear):
4539ab78691SEmil Tsalapatis 	 *
4549ab78691SEmil Tsalapatis 	 * <Clear>  <Poisoned>
4559ab78691SEmil Tsalapatis 	 * [ C C C ... P P ]
4569ab78691SEmil Tsalapatis 	 *
4579ab78691SEmil Tsalapatis 	 * This is so we can use a single number in [0, ASAN_SHADOW_SCALE)
4589ab78691SEmil Tsalapatis 	 * to represent the poison state of the granule.
4599ab78691SEmil Tsalapatis 	 */
4609ab78691SEmil Tsalapatis 	if (unlikely((u64)addr & ASAN_GRANULE_MASK))
4619ab78691SEmil Tsalapatis 		return -EINVAL;
4629ab78691SEmil Tsalapatis 
4639ab78691SEmil Tsalapatis 	shadow = mem_to_shadow(addr);
4649ab78691SEmil Tsalapatis 	len = size >> ASAN_SHADOW_SHIFT;
4659ab78691SEmil Tsalapatis 
4669ab78691SEmil Tsalapatis 	asan_memset(shadow, 0, len);
4679ab78691SEmil Tsalapatis 
4689ab78691SEmil Tsalapatis 	/*
4699ab78691SEmil Tsalapatis 	 * If we are allocating a non-granule aligned region, we need to adjust
4709ab78691SEmil Tsalapatis 	 * the last byte of the shadow map to list how many bytes in the granule
4719ab78691SEmil Tsalapatis 	 * are unpoisoned. If the region is aligned, then the memset call above
4729ab78691SEmil Tsalapatis 	 * was enough.
4739ab78691SEmil Tsalapatis 	 */
4749ab78691SEmil Tsalapatis 	if (partial)
4759ab78691SEmil Tsalapatis 		shadow[len] = partial;
4769ab78691SEmil Tsalapatis 
4779ab78691SEmil Tsalapatis 	return 0;
4789ab78691SEmil Tsalapatis }
4799ab78691SEmil Tsalapatis 
4809ab78691SEmil Tsalapatis /*
4819ab78691SEmil Tsalapatis  * Initialize ASAN state when necessary. Triggered from userspace before
4829ab78691SEmil Tsalapatis  * allocator startup.
4839ab78691SEmil Tsalapatis  */
4849ab78691SEmil Tsalapatis SEC("syscall")
4859ab78691SEmil Tsalapatis __weak __noasan int asan_init(struct asan_init_args *args)
4869ab78691SEmil Tsalapatis {
4879ab78691SEmil Tsalapatis 	u64 globals_pages = args->arena_globals_pages;
4889ab78691SEmil Tsalapatis 	u64 all_pages = args->arena_all_pages;
4899ab78691SEmil Tsalapatis 	u64 shadow_map, shadow_pgoff;
4909ab78691SEmil Tsalapatis 	u64 shadow_pages;
4919ab78691SEmil Tsalapatis 
4929ab78691SEmil Tsalapatis 	if (asan_inited)
4939ab78691SEmil Tsalapatis 		return 0;
4949ab78691SEmil Tsalapatis 
4959ab78691SEmil Tsalapatis 	/*
4969ab78691SEmil Tsalapatis 	 * Round up the shadow map size to the nearest page.
4979ab78691SEmil Tsalapatis 	 */
4989ab78691SEmil Tsalapatis 	shadow_pages = all_pages >> ASAN_SHADOW_SHIFT;
4999ab78691SEmil Tsalapatis 	if ((all_pages & ((1 << ASAN_SHADOW_SHIFT) - 1)))
5009ab78691SEmil Tsalapatis 		shadow_pages += 1;
5019ab78691SEmil Tsalapatis 
5029ab78691SEmil Tsalapatis 	if (all_pages > (1ULL << 32) / __PAGE_SIZE) {
5039ab78691SEmil Tsalapatis 		arena_stderr("error: arena size %lx too large", all_pages);
5049ab78691SEmil Tsalapatis 		return -EINVAL;
5059ab78691SEmil Tsalapatis 	}
5069ab78691SEmil Tsalapatis 
5079ab78691SEmil Tsalapatis 	if (globals_pages > all_pages) {
5089ab78691SEmil Tsalapatis 		arena_stderr("error: globals %lx do not fit in arena %lx",
5099ab78691SEmil Tsalapatis 				globals_pages, all_pages);
5109ab78691SEmil Tsalapatis 		return -EINVAL;
5119ab78691SEmil Tsalapatis 	}
5129ab78691SEmil Tsalapatis 
5139ab78691SEmil Tsalapatis 	if (globals_pages + shadow_pages >= all_pages) {
5149ab78691SEmil Tsalapatis 		arena_stderr("error: globals %lx do not leave room for shadow map %lx "
5159ab78691SEmil Tsalapatis 				"(arena pages %lx)",
5169ab78691SEmil Tsalapatis 				globals_pages, shadow_pages, all_pages);
5179ab78691SEmil Tsalapatis 		return -EINVAL;
5189ab78691SEmil Tsalapatis 	}
5199ab78691SEmil Tsalapatis 
5209ab78691SEmil Tsalapatis 	shadow_pgoff = all_pages - shadow_pages - globals_pages;
5219ab78691SEmil Tsalapatis 	__asan_shadow_memory_dynamic_address = shadow_pgoff * __PAGE_SIZE;
5229ab78691SEmil Tsalapatis 
5239ab78691SEmil Tsalapatis 	/*
5249ab78691SEmil Tsalapatis 	 * Allocate the last (1/ASAN_SHADOW_SCALE)th of an arena's pages for the map
5259ab78691SEmil Tsalapatis 	 * We find the offset and size from the arena map.
5269ab78691SEmil Tsalapatis 	 *
5279ab78691SEmil Tsalapatis 	 * The allocated map pages are zeroed out, meaning all memory is marked as valid
5289ab78691SEmil Tsalapatis 	 * even if it's not allocated already. This is expected: Since the actual memory
5299ab78691SEmil Tsalapatis 	 * pages are not allocated, accesses to it will trigger page faults and will be
5309ab78691SEmil Tsalapatis 	 * reported through BPF streams. Any pages allocated through bpf_arena_alloc_pages
5319ab78691SEmil Tsalapatis 	 * should be poisoned by the allocator right after the call succeeds.
5329ab78691SEmil Tsalapatis 	 */
5339ab78691SEmil Tsalapatis 	shadow_map = (u64)bpf_arena_alloc_pages(
5349ab78691SEmil Tsalapatis 		&arena, (void __arena *)__asan_shadow_memory_dynamic_address,
5359ab78691SEmil Tsalapatis 		shadow_pages, NUMA_NO_NODE, 0);
5369ab78691SEmil Tsalapatis 	if (!shadow_map) {
5379ab78691SEmil Tsalapatis 		arena_stderr("Could not allocate shadow map\n");
5389ab78691SEmil Tsalapatis 
5399ab78691SEmil Tsalapatis 		__asan_shadow_memory_dynamic_address = 0;
5409ab78691SEmil Tsalapatis 
5419ab78691SEmil Tsalapatis 		return -ENOMEM;
5429ab78691SEmil Tsalapatis 	}
5439ab78691SEmil Tsalapatis 
5449ab78691SEmil Tsalapatis 	asan_inited = true;
5459ab78691SEmil Tsalapatis 
5469ab78691SEmil Tsalapatis 	return 0;
5479ab78691SEmil Tsalapatis }
5489ab78691SEmil Tsalapatis 
5499ab78691SEmil Tsalapatis #pragma clang attribute pop
5509ab78691SEmil Tsalapatis 
5519ab78691SEmil Tsalapatis #endif /* BPF_ARENA_ASAN */
5529ab78691SEmil Tsalapatis 
5539ab78691SEmil Tsalapatis __weak char _license[] SEC("license") = "GPL";
554