* [PATCH bpf-next 1/2] bpf: Reject out-of-range arena free addresses
2026-07-06 15:52 [PATCH bpf-next 0/2] bpf: Reject out-of-range arena free addresses Nuoqi Gui
@ 2026-07-06 15:52 ` Nuoqi Gui
2026-07-06 16:57 ` Emil Tsalapatis
2026-07-06 15:52 ` [PATCH bpf-next 2/2] selftests/bpf: Cover " Nuoqi Gui
1 sibling, 1 reply; 4+ messages in thread
From: Nuoqi Gui @ 2026-07-06 15:52 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Shuah Khan
Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Puranjay Mohan, bpf, linux-kernel,
linux-kselftest, Nuoqi Gui
bpf_arena_free_pages() accepts scalar arena addresses. The free path
masks the low 32 bits, computes the full user address, and clips only
against arena->user_vm_end before inserting the range into the arena
free range tree.
A scalar address below arena->user_vm_start can therefore produce an
out-of-domain page offset. A later allocation can consume that offset
from the free range tree and return an address outside the arena range.
Reject frees whose computed full address is below the arena start before
clipping the end of the range.
Fixes: 317460317a02 ("bpf: Introduce bpf_arena.")
Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
---
kernel/bpf/arena.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/kernel/bpf/arena.c b/kernel/bpf/arena.c
index 80b7b8a69446..97a5d8d21295 100644
--- a/kernel/bpf/arena.c
+++ b/kernel/bpf/arena.c
@@ -853,6 +853,8 @@ static void arena_free_pages(struct bpf_arena *arena, long uaddr, long page_cnt,
uaddr &= PAGE_MASK;
kaddr = bpf_arena_get_kern_vm_start(arena) + uaddr;
full_uaddr = clear_lo32(arena->user_vm_start) + uaddr;
+ if (full_uaddr < arena->user_vm_start)
+ return;
uaddr_end = min(arena->user_vm_end, full_uaddr + (page_cnt << PAGE_SHIFT));
if (full_uaddr >= uaddr_end)
return;
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread* [PATCH bpf-next 2/2] selftests/bpf: Cover out-of-range arena free addresses
2026-07-06 15:52 [PATCH bpf-next 0/2] bpf: Reject out-of-range arena free addresses Nuoqi Gui
2026-07-06 15:52 ` [PATCH bpf-next 1/2] " Nuoqi Gui
@ 2026-07-06 15:52 ` Nuoqi Gui
1 sibling, 0 replies; 4+ messages in thread
From: Nuoqi Gui @ 2026-07-06 15:52 UTC (permalink / raw)
To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko,
Eduard Zingerman, Kumar Kartikeya Dwivedi, Shuah Khan
Cc: Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
Emil Tsalapatis, Puranjay Mohan, bpf, linux-kernel,
linux-kselftest, Nuoqi Gui
Add a verifier selftest that fills a two-page arena, frees the page below
the arena start through a scalar value, and checks that a third allocation
is still unavailable.
Without the arena free range check, the out-of-range free can be inserted
into the free range tree and then allocated again, so the test returns 3.
With the fix, the out-of-range free is ignored and the focused test passes.
Signed-off-by: Nuoqi Gui <gnq25@mails.tsinghua.edu.cn>
---
tools/testing/selftests/bpf/progs/verifier_arena.c | 26 ++++++++++++++++++++++
1 file changed, 26 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index df0e22d1a29b..b21a8895804f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -11,6 +11,7 @@
#include <bpf_arena_common.h>
#define private(name) SEC(".bss." #name) __hidden __attribute__((aligned(8)))
+#define ARENA_VM_START_L32 (~0u - __PAGE_SIZE * 2 + 1)
struct {
__uint(type, BPF_MAP_TYPE_ARENA);
@@ -93,6 +94,31 @@ int basic_alloc1(void *ctx)
return 0;
}
+SEC("syscall")
+__success __retval(0)
+int free_pages_out_of_bounds(void *ctx)
+{
+#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
+ __u64 page1, page2, no_page;
+ __u64 below_start = (__u32)(ARENA_VM_START_L32 - __PAGE_SIZE);
+
+ page1 = (__u64)bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+ if (!page1)
+ return 1;
+ page2 = (__u64)bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+ if (!page2)
+ return 2;
+
+ asm volatile("" : "+r"(below_start));
+ bpf_arena_free_pages(&arena, (void __arena *)below_start, 1);
+
+ no_page = (__u64)bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+ if (no_page)
+ return 3;
+#endif
+ return 0;
+}
+
SEC("socket")
__success __retval(0)
int basic_alloc2_nosleep(void *ctx)
--
2.34.1
^ permalink raw reply related [flat|nested] 4+ messages in thread