* [PATCH bpf-next v1 7/7] selftests/bpf: write the mixed arena/scalar ALU tests in asm
@ 2026-10-28 0:04 Vineet Gupta
0 siblings, 0 replies; only message in thread
From: Vineet Gupta @ 2026-10-28 0:04 UTC (permalink / raw)
To: bpf
Cc: ast, daniel, andrii, eddyz87, memxor, martin.lau, song,
yonghong.song, jolsa, emil, ihor.solodrai, john.fastabend, shuah,
linux-kernel, linux-kselftest, jose.marchesi, david.faust,
Vineet Gupta
They fail to build with BPF_GCC:
progs/verifier_arena.c:566:5: error: optimization may eliminate reads and/or writes to register variables [-Werror=volatile-register-var]
for
volatile register __u64 reg asm("r3");
GCC means it: with the warning silenced it drops both scalar accesses,
leaving nothing that uses r3 as a scalar:
call 7
if w0 == 0 goto .L136
r9 = arena ll;r3 = 0;r3 = addr_space_cast(r3, 0, 1);
.L136:
r0 = 0
exit
`reg = 1ULL << 32' and `reg += 1' are both gone. The tests need one
instruction reached with r3 live as a scalar on one path and as an
arena pointer on the other; what is left gives the verifier no reason
to reject anything, so the __failure expectation would not hold. A
local register variable is not guaranteed to survive a helper call
either, which GCC is also entitled to act on.
Nothing here needs a compiler to allocate registers, so write the
whole body in asm like mixed_arena_stack_atomic below, which tests the
same shape. Both compilers then emit the same sequence, with the
branch landing on the shared ALU op:
call 7
r6=r0
r3=1
r3<<=32
if w6==0 goto 4 <- lands on r3+=1
r9=0 ll
r3=0
r3 = addr_space_cast(r3, 0, 1)
r3+=1 <- scalar on one path, arena pointer on the other
r0=0
exit
Take __load_if_JITed() from the sibling while here. A program binding
an arena map is rejected in check_map_prog_compatibility() with "JIT is
required to use arena" during map resolution, before do_check() can
reach the shared instruction, so without a JIT these would fail on the
wrong message rather than skip.
Not exercised by a stock bpf-gcc, which compiles both tests out with the
rest of the arena block; the build failure above needs a compiler that
has arena support. Under clang the rewrite is not a no-op either: the
pre-image spilled r3 to the stack and did the shared ALU op on r1
reloaded from it, so the test was not exercising one register live as
both a scalar and an arena pointer.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
.../selftests/bpf/progs/verifier_arena.c | 85 ++++++++++---------
1 file changed, 45 insertions(+), 40 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index 21b66d51c61e..b84d8f1e0b39 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -539,53 +539,58 @@ int arena_ptr_add_arena_ptr(void *ctx)
return 0;
}
+/*
+ * The r9 load is never read; it is there so libbpf records the arena map
+ * as used, which is what lets the verifier accept addr_space_cast at all.
+ */
SEC("syscall")
__failure __msg("same insn cannot be used with and without arena pointer")
-int mixed_arena_scalar_alu64_scalar_first(void *ctx)
-{
- volatile register __u64 reg asm("r3");
- __u32 pick_arena = bpf_get_prandom_u32();
-
- reg = 1ULL << 32;
-
- if (pick_arena) {
- asm volatile (
- "r9 = %[arena] ll;"
- "%[reg] = 0;"
- "%[reg] = addr_space_cast(%[reg], 0x0, 0x1);"
- : [reg] "=r"(reg)
- : __imm_addr(arena)
- : "r9"
- );
- }
-
- reg += 1;
-
- return 0;
+__load_if_JITed()
+__naked void mixed_arena_scalar_alu64_scalar_first(void)
+{
+ asm volatile (" \
+ call %[bpf_get_prandom_u32]; \
+ r6 = r0; \
+ r3 = 1; \
+ r3 <<= 32; \
+ if w6 == 0 goto 1f; \
+ r9 = %[arena] ll; \
+ r3 = 0; \
+ r3 = addr_space_cast(r3, 0, 1); \
+1: r3 += 1; \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm_addr(arena),
+ __imm(bpf_get_prandom_u32)
+ : __clobber_all);
}
+/*
+ * The r9 load is never read; it is there so libbpf records the arena map
+ * as used, which is what lets the verifier accept addr_space_cast at all.
+ */
SEC("syscall")
__failure __msg("same insn cannot be used with and without arena pointer")
-int mixed_arena_scalar_alu64_arena_first(void *ctx)
+__load_if_JITed()
+__naked void mixed_arena_scalar_alu64_arena_first(void)
{
- volatile register __u64 reg asm("r3");
- __u32 pick_scalar = bpf_get_prandom_u32();
-
- asm volatile (
- "r9 = %[arena] ll;"
- "%[reg] = 0;"
- "%[reg] = addr_space_cast(%[reg], 0x0, 0x1);"
- : [reg] "=r"(reg)
- : __imm_addr(arena)
- : "r9"
- );
-
- if (pick_scalar)
- reg = 1ULL << 32;
-
- reg += 1;
-
- return 0;
+ asm volatile (" \
+ call %[bpf_get_prandom_u32]; \
+ r6 = r0; \
+ r9 = %[arena] ll; \
+ r3 = 0; \
+ r3 = addr_space_cast(r3, 0, 1); \
+ if w6 == 0 goto 1f; \
+ r3 = 1; \
+ r3 <<= 32; \
+1: r3 += 1; \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm_addr(arena),
+ __imm(bpf_get_prandom_u32)
+ : __clobber_all);
}
SEC("syscall")
--
2.53.0-Meta
^ permalink raw reply related [flat|nested] only message in thread
only message in thread, other threads:[~2026-10-28 0:05 UTC | newest]
Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-10-28 0:04 [PATCH bpf-next v1 7/7] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.