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