On Tue Jun 30, 2026 at 6:12 AM EDT, Yiyang Chen wrote: > Add a verifier_arena case that fills a two-page arena, calls > bpf_arena_free_pages() with a scalar address one page below the arena > base, and then verifies that another allocation is still rejected. > > Before the runtime guard, the invalid free can repopulate the free > tree with an out-of-domain offset and the final allocation succeeds. > > Signed-off-by: Yiyang Chen <[email protected]>
Reviewed-by: Emil Tsalapatis <[email protected]> Nit/question below. > --- > .../selftests/bpf/progs/verifier_arena.c | 41 ++++++++++++++++--- > 1 file changed, 36 insertions(+), 5 deletions(-) > > diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c > b/tools/testing/selftests/bpf/progs/verifier_arena.c > index 62e282f4448aa..b4bd134646607 100644 > --- a/tools/testing/selftests/bpf/progs/verifier_arena.c > +++ b/tools/testing/selftests/bpf/progs/verifier_arena.c > @@ -12,15 +12,17 @@ > > #define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8))) > > +#ifdef __TARGET_ARCH_arm64 > +#define ARENA_VM_START ((1ull << 32) | (~0u - __PAGE_SIZE * 2 + 1)) > +#else > +#define ARENA_VM_START ((1ull << 44) | (~0u - __PAGE_SIZE * 2 + 1)) > +#endif > + > struct { > __uint(type, BPF_MAP_TYPE_ARENA); > __uint(map_flags, BPF_F_MMAPABLE); > __uint(max_entries, 2); /* arena of two pages close to 32-bit boundary*/ > -#ifdef __TARGET_ARCH_arm64 > - __ulong(map_extra, (1ull << 32) | (~0u - __PAGE_SIZE * 2 + 1)); /* > start of mmap() region */ > -#else > - __ulong(map_extra, (1ull << 44) | (~0u - __PAGE_SIZE * 2 + 1)); /* > start of mmap() region */ > -#endif > + __ulong(map_extra, ARENA_VM_START); /* start of mmap() region */ > } arena SEC(".maps"); > > SEC("socket") > @@ -93,6 +95,35 @@ int basic_alloc1(void *ctx) > return 0; > } > > +SEC("syscall") > +__success __retval(0) > +int free_scalar_below_arena(void *ctx) > +{ > + void __arena *page1, *page2, *page3; > + __u64 bad_addr = ARENA_VM_START - __PAGE_SIZE; > + > + page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); > + if (!page1) > + return 1; > + > + page2 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); > + if (!page2) > + return 2; > + > + page3 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); > + if (page3) > + return 3; > + > + asm volatile("" : "+r"(bad_addr)); Why the asm volatile? We use it right underneath, what does this give us. > + bpf_arena_free_pages(&arena, (void __arena *)bad_addr, 1); > + > + page3 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0); > + if (page3) > + return 4; > + > + return 0; > +} > + > SEC("socket") > __success __retval(0) > int basic_alloc2_nosleep(void *ctx)

