Re: [PATCH bpf-next 2/2] selftests/bpf: Cover scalar arena frees below the base
From: Emil Tsalapatis
Date: Wed Jul 01 2026 - 17:19:26 EST
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 <chenyy23@xxxxxxxxxxxxxxxxxxxxx>
Reviewed-by: Emil Tsalapatis <emil@xxxxxxxxxxxxxxx>
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)