// SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */ #define BPF_NO_KFUNC_PROTOTYPES #include #include #include "bpf_experimental.h" #include #include "../test_kmods/bpf_testmod.h" #include "../test_kmods/bpf_testmod_kfunc.h" char _license[] SEC("license") = "GPL"; struct { __uint(type, BPF_MAP_TYPE_ARENA); __uint(map_flags, BPF_F_MMAPABLE); /* page 0 hosts the arena globals, page 1 is for allocations */ __uint(max_entries, 2); } arena SEC(".maps"); /* also associates the callbacks with the arena */ u64 __arena arena_touch; /* raw value of the last __arena ctx argument, captured by test_arena_cb */ u64 __arena cb_ptr_val; SEC("struct_ops/test_arena") int test_arena_cb(unsigned long long *ctx) { u64 __arena *ptr = (u64 __arena *)ctx[0]; arena_touch++; cb_ptr_val = ctx[0]; *ptr += 1; return 0; } SEC("struct_ops/test_arena_nullable") int test_arena_nullable_cb(unsigned long long *ctx) { u64 __arena *ptr = (u64 __arena *)ctx[0]; arena_touch++; if (!ptr) return 0xbee; *ptr += 1; return 0; } SEC("struct_ops/test_arena_stack") int test_arena_stack_cb(unsigned long long *ctx) { u64 __arena *ptr = (u64 __arena *)ctx[8]; arena_touch++; /* pin the slot layout: the leading args fill ctx[0]..ctx[7] */ if (ctx[0] != 1 || ctx[7] != 8) return 0xbad; *ptr += 1; return 0; } SEC("struct_ops/test_arena_multislot") int test_arena_multislot_cb(unsigned long long *ctx) { u64 __arena *ptr = (u64 __arena *)ctx[2]; arena_touch++; /* * The 16-byte struct occupies ctx[0] and ctx[1], so @ptr is argument * one but slot two. Getting that wrong hands the callback a scalar. */ if (ctx[0] != 11 || ctx[1] != 22) return 0xbad; *ptr += 1; return 0; } SEC(".struct_ops.link") struct bpf_testmod_ops3 testmod_arena = { .test_arena = (void *)test_arena_cb, .test_arena_nullable = (void *)test_arena_nullable_cb, .test_arena_stack = (void *)test_arena_stack_cb, .test_arena_multislot = (void *)test_arena_multislot_cb, }; SEC("syscall") int trigger(void *ctx) { #if defined(__BPF_FEATURE_ADDR_SPACE_CAST) u64 __arena *val; int ret; val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); if (!val) return 1; *val = 41; ret = bpf_testmod_ops3_call_test_arena((u64 *)val); if (ret) return 2; if (*val != 42) return 3; /* * The callback must have seen exactly (u32)(kaddr - kern_vm_start), * which is the arena offset of val with the upper 32 bits clear. */ if (cb_ptr_val != (u32)(u64)val) return 4; ret = bpf_testmod_ops3_call_test_arena_nullable((u64 *)val); if (ret) return 5; if (*val != 43) return 6; /* NULL survives the nullable kfunc and the trampoline as NULL */ ret = bpf_testmod_ops3_call_test_arena_nullable(NULL); if (ret != 0xbee) return 7; /* the arena pointer is stack-passed into the trampoline here */ ret = bpf_testmod_ops3_call_test_arena_stack((u64 *)val); if (ret) return 8; if (*val != 44) return 9; /* a multi-slot arg precedes the arena pointer here */ ret = bpf_testmod_ops3_call_test_arena_multislot((u64 *)val); if (ret) return 10; if (*val != 45) return 11; bpf_arena_free_pages(&arena, (void __arena *)val, 1); #endif return 0; }