* [PATCH bpf-next v4 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
` (7 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
Commit 7cd1f73b37ea ("selftests/bpf: Fix build of callx tests with GCC")
gated the callx progs on __clang__. GCC had indirect call support which was
recently refurbished along with a __BPF_FEATURE_CALLX macro [1], so gate
on that as well so once this GCC is released, the tests start passing.
Put the arch and compiler conditions in a CAN_USE_CALLX macro in
bpf_misc.h, and use it in all the test programs.
While at it, mark the placeholder programs left for unsupported
toolchains and arches with __skip(), so they report SKIP rather than OK.
[1] https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=ffe3ead4a8ad704b557b0cfa4f823df647c65a0d
Measured on bpf-next with a stock bpf-gcc that defines
__BPF_FEATURE_CALLX, test_progs before and after. 79 subtests that did
not exist in the report now run, 78 OK and one @unpriv SKIP, with no
failures. The placeholders retire and the lskel test recovers:
verifier_callx/dummy OK -> (absent)
verifier_callx_rodata/dummy OK -> (absent)
verifier_callx_rodata/callx_rodata_needs_perfmon @unpriv -> SKIP
callx_rodata_lskel SKIP -> OK
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: unchanged.
.../testing/selftests/bpf/prog_tests/callx_rodata_lskel.c | 2 +-
tools/testing/selftests/bpf/progs/bpf_misc.h | 7 +++++++
tools/testing/selftests/bpf/progs/callx_rodata.c | 4 ++--
tools/testing/selftests/bpf/progs/verifier_callx.c | 5 ++---
tools/testing/selftests/bpf/progs/verifier_callx_rodata.c | 5 ++---
5 files changed, 14 insertions(+), 9 deletions(-)
diff --git a/tools/testing/selftests/bpf/prog_tests/callx_rodata_lskel.c b/tools/testing/selftests/bpf/prog_tests/callx_rodata_lskel.c
index f6ef67c3141e..acd821fb568f 100644
--- a/tools/testing/selftests/bpf/prog_tests/callx_rodata_lskel.c
+++ b/tools/testing/selftests/bpf/prog_tests/callx_rodata_lskel.c
@@ -36,7 +36,7 @@ static void __test_callx_rodata_lskel(void)
if (!ASSERT_OK_PTR(skel, "open"))
return;
- /* gcc doesn't support indirect calls */
+ /* toolchain or JIT can't do callx */
if (skel->rodata->skip) {
test__skip();
goto out;
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index d7f706c3bc4a..1b264480e128 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -295,6 +295,13 @@
#define CAN_USE_LOAD_ACQ_STORE_REL
#endif
+/* clang always emits callx, gcc only when it defines __BPF_FEATURE_CALLX */
+#if (defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86) || \
+ (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && \
+ (defined(__clang__) || defined(__BPF_FEATURE_CALLX))
+#define CAN_USE_CALLX
+#endif
+
#if defined(__TARGET_ARCH_arm64) || defined(__TARGET_ARCH_x86)
#define SPEC_V1
#endif
diff --git a/tools/testing/selftests/bpf/progs/callx_rodata.c b/tools/testing/selftests/bpf/progs/callx_rodata.c
index e3f8335f80f8..8c28f01b7113 100644
--- a/tools/testing/selftests/bpf/progs/callx_rodata.c
+++ b/tools/testing/selftests/bpf/progs/callx_rodata.c
@@ -4,13 +4,14 @@
#include <stdbool.h>
#include <linux/bpf.h>
#include <bpf/bpf_helpers.h>
+#include "bpf_misc.h"
/* set by user space before the programs are loaded, it's in .rodata too */
const volatile int bias = 1;
int op_idx;
-#ifdef __clang__
+#ifdef CAN_USE_CALLX
const volatile bool skip = false;
@@ -48,7 +49,6 @@ int both_ops(void *ctx)
#else
-/* gcc doesn't support indirect calls */
const volatile bool skip = true;
SEC("socket")
diff --git a/tools/testing/selftests/bpf/progs/verifier_callx.c b/tools/testing/selftests/bpf/progs/verifier_callx.c
index 7e59188d9fca..ca1aef34ab72 100644
--- a/tools/testing/selftests/bpf/progs/verifier_callx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_callx.c
@@ -6,9 +6,7 @@
#include "bpf_misc.h"
#include "../../../include/linux/filter.h"
-/* gcc doesn't support indirect calls */
-#if defined(__clang__) && (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) || \
- (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64))
+#ifdef CAN_USE_CALLX
#define CALLX_INSN(DST, SRC, OFF, IMM) \
BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, DST, SRC, OFF, IMM)
@@ -1075,6 +1073,7 @@ int callx_c_ops_on_stack(void *ctx)
#else
SEC("socket")
+__skip("callx needs arch JIT and compiler support")
__success
int dummy(void *ctx)
{
diff --git a/tools/testing/selftests/bpf/progs/verifier_callx_rodata.c b/tools/testing/selftests/bpf/progs/verifier_callx_rodata.c
index b2ee0c8e042b..642071fa7498 100644
--- a/tools/testing/selftests/bpf/progs/verifier_callx_rodata.c
+++ b/tools/testing/selftests/bpf/progs/verifier_callx_rodata.c
@@ -6,9 +6,7 @@
#include "bpf_misc.h"
#include "../../../include/linux/filter.h"
-/* gcc doesn't support indirect calls */
-#if defined(__clang__) && (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) || \
- (defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64))
+#ifdef CAN_USE_CALLX
/*
* Read-only data with pointers to functions, where the compiler puts tables
@@ -992,6 +990,7 @@ int callx_c_packed_struct(void *ctx)
#else
SEC("socket")
+__skip("callx needs arch JIT and compiler support")
__success
int dummy(void *ctx)
{
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v4 2/8] selftests/bpf: don't pass clang-only warning flags to GCC
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 3/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
` (6 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
The clang only toggles are passed even for for BPF_GCC builds.
GCC accepts an unknown -Wno-foo silently until some other diagnostic fires,
and then appends a note to it, so every warning in a bpf_gcc build comes with
two lines of noise:
cc1: note: unrecognized command-line option '-Wno-initializer-overrides' may have been intended to silence earlier diagnostics
cc1: note: unrecognized command-line option '-Wno-microsoft-anon-tag' may have been intended to silence earlier diagnostics
Collect them in CLANG_BPF_CFLAGS and hand them only to the clang
flavors, through the per-flavor BPF_CC_FLAGS that Makefile.skel appends
after BPF_CFLAGS.
Only these two are clang-only; -Wno-compare-distinct-pointer-types and
-fms-extensions are understood by GCC 14 and later and stay in
BPF_CFLAGS.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: unchanged.
tools/testing/selftests/bpf/Makefile | 8 ++++----
tools/testing/selftests/bpf/Makefile.buildvars | 6 ++++--
2 files changed, 8 insertions(+), 6 deletions(-)
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index c99e0f623e10..77682bf28c6e 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -376,7 +376,7 @@ CLANG_TARGET_ARCH = --target=$(notdir $(CROSS_COMPILE:%-=%))
endif
CLANG_SYS_INCLUDES = $(call get_sys_includes,$(CLANG),$(CLANG_TARGET_ARCH))
-CLANG_CFLAGS = $(CLANG_SYS_INCLUDES)
+CLANG_CFLAGS = $(CLANG_SYS_INCLUDES) $(CLANG_BPF_CFLAGS)
# genkey writes both outputs. Pattern rules group them even on make
# versions before 4.3, preventing concurrent genkey invocations.
@@ -511,7 +511,7 @@ BINARY := test_progs
BPF_CC := $(CLANG)
BPF_CC_MSG := CLNG-BPF
BPF_SYS_INCLUDES := $(CLANG_SYS_INCLUDES)
-BPF_CC_FLAGS := -O2 $(BPF_TARGET_ENDIAN) -mcpu=v3
+BPF_CC_FLAGS := -O2 $(BPF_TARGET_ENDIAN) -mcpu=v3 $(CLANG_BPF_CFLAGS)
BPF_DEFINES := -DENABLE_ATOMICS_TESTS
include Makefile.skel
@@ -527,12 +527,12 @@ $(OUTPUT)/test_progs: $(RUNNER_PREREQS) $(BPF_OBJS) $(ALL_SKELS) FORCE
$(OUTPUT)/test_progs-no_alu32: $(RUNNER_PREREQS) FORCE
+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=no_alu32 TESTS_DIR=prog_tests \
- $(CLANG_RUNNER_ARGS) BPF_CC_FLAGS='-O2 $(BPF_TARGET_ENDIAN) -mcpu=v2'
+ $(CLANG_RUNNER_ARGS) BPF_CC_FLAGS='-O2 $(BPF_TARGET_ENDIAN) -mcpu=v2 $(CLANG_BPF_CFLAGS)'
ifneq ($(CLANG_CPUV4),)
$(OUTPUT)/test_progs-cpuv4: $(RUNNER_PREREQS) FORCE
+$(Q)$(RUNNER_MAKE) RUNNER=test_progs FLAVOR=cpuv4 TESTS_DIR=prog_tests \
- $(CLANG_RUNNER_ARGS) BPF_CC_FLAGS='-O2 $(BPF_TARGET_ENDIAN) -mcpu=v4' \
+ $(CLANG_RUNNER_ARGS) BPF_CC_FLAGS='-O2 $(BPF_TARGET_ENDIAN) -mcpu=v4 $(CLANG_BPF_CFLAGS)' \
BPF_DEFINES=-DENABLE_ATOMICS_TESTS
endif
diff --git a/tools/testing/selftests/bpf/Makefile.buildvars b/tools/testing/selftests/bpf/Makefile.buildvars
index d2a0c0031b87..4d25a736376c 100644
--- a/tools/testing/selftests/bpf/Makefile.buildvars
+++ b/tools/testing/selftests/bpf/Makefile.buildvars
@@ -132,13 +132,15 @@ BPF_CFLAGS = -g -Wall -Werror -D__TARGET_ARCH_$(SRCARCH) $(MENDIAN) \
-I$(abspath $(OUTPUT)/../usr/include) \
-std=gnu11 \
-fno-strict-aliasing \
- -Wno-microsoft-anon-tag \
-fms-extensions \
-Wno-compare-distinct-pointer-types \
- -Wno-initializer-overrides \
#
# TODO: enable me -Wsign-compare
+# Warning flags that only clang has. GCC accepts an unknown -Wno-foo
+# silently and then notes it on the next diagnostic it emits.
+CLANG_BPF_CFLAGS := -Wno-microsoft-anon-tag -Wno-initializer-overrides
+
VERIFY_SIG_SETUP := $(CURDIR)/verify_sig_setup.sh
VERIFICATION_CERT := $(BUILD_DIR)/signing_key.der
PRIVATE_KEY := $(BUILD_DIR)/signing_key.pem
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v4 3/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 4/8] selftests/bpf: spell the addr_space_cast address spaces in decimal Vineet Gupta
` (5 subsequent siblings)
8 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
arena_base() casts the map definition to the kernel's struct bpf_arena
and reads user_vm_start out of it. In the object file that definition
is only the 32-byte BTF placeholder the loader rewrites, so the access
is past the end of the object which GCC correctly complaints about and
due to -Werror leads to build failure:
libarena/include/bpf_arena_common.h:52:69: error: array subscript 'struct bpf_arena[0]' is partly outside array bounds of 'struct <anonymous>[1]' [-Werror=array-bounds=]
52 | #define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start)
progs/arena_race.c:58:30: note: in expansion of macro 'arena_base'
progs/stream.c already solved this for its own open-coded copy of the
access, with barrier_var(), for both the compilers. Do the same in
arena_base() itself so all such uses are fixed.
No test changes verdict; this only lets the objects build.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: was 4/8. barrier_var() for both compilers rather than
behind #ifdef __clang__, and stream.c's five open-coded copies of the
access fold back into the macro. (Alexei)
.../bpf/libarena/include/bpf_arena_common.h | 11 +++-
tools/testing/selftests/bpf/progs/stream.c | 56 +++----------------
2 files changed, 18 insertions(+), 49 deletions(-)
diff --git a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h
index 82aafe879fae..0eba52961405 100644
--- a/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h
+++ b/tools/testing/selftests/bpf/libarena/include/bpf_arena_common.h
@@ -49,7 +49,16 @@ void __arena* bpf_arena_alloc_pages(void *map, void __arena *addr, __u32 page_cn
int bpf_arena_reserve_pages(void *map, void __arena *addr, __u32 page_cnt) __ksym __weak;
void bpf_arena_free_pages(void *map, void __arena *ptr, __u32 page_cnt) __ksym __weak;
-#define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start)
+/*
+ * The map definition is smaller than struct bpf_arena.
+ * barrier_var() hides that from -Warray-bounds of GCC.
+ */
+#define arena_base(map) \
+ ({ \
+ struct bpf_arena *___arena = (void *)(map); \
+ barrier_var(___arena); \
+ (void __arena *)___arena->user_vm_start; \
+ })
#else /* when compiled as user space code */
diff --git a/tools/testing/selftests/bpf/progs/stream.c b/tools/testing/selftests/bpf/progs/stream.c
index 1ae015c4e6b4..b4d88d0e01ad 100644
--- a/tools/testing/selftests/bpf/progs/stream.c
+++ b/tools/testing/selftests/bpf/progs/stream.c
@@ -178,16 +178,8 @@ __stderr("Call trace:\n"
"|[ \t]+[^\n]+\n)*}}")
int stream_arena_write_fault(void *ctx)
{
- struct bpf_arena *ptr = (void *)&arena;
- u64 user_vm_start;
+ u64 user_vm_start = (u64)arena_base(&arena);
- /* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
- * triggers bounds checking since the map definition is smaller than struct
- * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
- * bounds analysis
- */
- barrier_var(ptr);
- user_vm_start = ptr->user_vm_start;
fault_addr = user_vm_start + 0x7fff;
bpf_addr_space_cast(user_vm_start, 0, 1);
asm volatile (
@@ -212,16 +204,8 @@ __stderr("Call trace:\n"
"|[ \t]+[^\n]+\n)*}}")
int stream_arena_read_fault(void *ctx)
{
- struct bpf_arena *ptr = (void *)&arena;
- u64 user_vm_start;
+ u64 user_vm_start = (u64)arena_base(&arena);
- /* Prevent GCC bounds warning: casting &arena to struct bpf_arena *
- * triggers bounds checking since the map definition is smaller than struct
- * bpf_arena. barrier_var() makes the pointer opaque to GCC, preventing the
- * bounds analysis
- */
- barrier_var(ptr);
- user_vm_start = ptr->user_vm_start;
fault_addr = user_vm_start + 0x7fff;
bpf_addr_space_cast(user_vm_start, 0, 1);
asm volatile (
@@ -252,17 +236,9 @@ int stream_arena_load_acquire_fault(void *ctx)
.off = 0x7fff,
.imm = 0x100, /* BPF_LOAD_ACQ */
};
- struct bpf_arena *ptr = (void *)&arena;
- u64 user_vm_start, val;
+ u64 user_vm_start = (u64)arena_base(&arena);
+ u64 val;
- /*
- * Prevent GCC bounds warning: casting &arena to struct bpf_arena *
- * triggers bounds checking since the map definition is smaller than
- * struct bpf_arena. barrier_var() makes the pointer opaque to GCC,
- * preventing the bounds analysis.
- */
- barrier_var(ptr);
- user_vm_start = ptr->user_vm_start;
fault_addr = user_vm_start + 0x7fff;
bpf_addr_space_cast(user_vm_start, 0, 1);
asm volatile (
@@ -296,17 +272,9 @@ int stream_arena_xchg_fault(void *ctx)
.off = 0x7fff,
.imm = 0xe1, /* BPF_XCHG */
};
- struct bpf_arena *ptr = (void *)&arena;
- u64 user_vm_start, val;
+ u64 user_vm_start = (u64)arena_base(&arena);
+ u64 val;
- /*
- * Prevent GCC bounds warning: casting &arena to struct bpf_arena *
- * triggers bounds checking since the map definition is smaller than
- * struct bpf_arena. barrier_var() makes the pointer opaque to GCC,
- * preventing the bounds analysis.
- */
- barrier_var(ptr);
- user_vm_start = ptr->user_vm_start;
fault_addr = user_vm_start + 0x7fff;
bpf_addr_space_cast(user_vm_start, 0, 1);
/*
@@ -346,17 +314,9 @@ int stream_arena_cmpxchg_fault(void *ctx)
.off = 0x7fff,
.imm = 0xf1, /* BPF_CMPXCHG */
};
- struct bpf_arena *ptr = (void *)&arena;
- u64 user_vm_start, val;
+ u64 user_vm_start = (u64)arena_base(&arena);
+ u64 val;
- /*
- * Prevent GCC bounds warning: casting &arena to struct bpf_arena *
- * triggers bounds checking since the map definition is smaller than
- * struct bpf_arena. barrier_var() makes the pointer opaque to GCC,
- * preventing the bounds analysis.
- */
- barrier_var(ptr);
- user_vm_start = ptr->user_vm_start;
fault_addr = user_vm_start + 0x7fff;
bpf_addr_space_cast(user_vm_start, 0, 1);
/*
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v4 4/8] selftests/bpf: spell the addr_space_cast address spaces in decimal
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
` (2 preceding siblings ...)
2026-11-03 18:18 ` [PATCH bpf-next v4 3/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:57 ` bot+bpf-ci
2026-11-03 18:18 ` [PATCH bpf-next v4 5/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
` (4 subsequent siblings)
8 siblings, 1 reply; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
The inline asm in the arena tests writes the two address spaces of
addr_space_cast() in hex which trips up a GNU assembler bug:
progs/jit_insns_arena.c:49: Error: unrecognized instruction `r7 =addr_space_cast(r7,0x0,0x1)'
progs/jit_insns_arena.c:49: Error: expected ',', got 'x'
This has been fixed and landed binutils commit 835aee81fa93 ("bpf: accept
any spelling of the addr_space_cast address spaces"), but will only land
in the 2.47 release, so add the workarounds for now.
A GCC that defines __BPF_FEATURE_ADDR_SPACE_CAST would be paired with
a capable binutils.
No functional change.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: new patch.
.../selftests/bpf/progs/arena_atomics.c | 32 +++++++++----------
.../selftests/bpf/progs/jit_insns_arena.c | 8 ++---
.../selftests/bpf/progs/verifier_arena.c | 16 +++++-----
.../selftests/bpf/progs/verifier_hmul_arena.c | 4 +--
.../selftests/bpf/progs/verifier_ldsx.c | 10 +++---
5 files changed, 35 insertions(+), 35 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/arena_atomics.c b/tools/testing/selftests/bpf/progs/arena_atomics.c
index ce76a73d197b..2a9664f54399 100644
--- a/tools/testing/selftests/bpf/progs/arena_atomics.c
+++ b/tools/testing/selftests/bpf/progs/arena_atomics.c
@@ -227,11 +227,11 @@ int fetch_r0(const void *ctx)
#if defined(ENABLE_ATOMICS_TESTS) && defined(__BPF_FEATURE_ADDR_SPACE_CAST)
asm volatile (
"r1 = %[fetch_src_r0_value] ll;"
- "r1 = addr_space_cast(r1, 0x0, 0x1);"
+ "r1 = addr_space_cast(r1, 0, 1);"
"r0 = 0x1100000011 ll;"
".8byte %[fetch_src_r0_insn];"
"r2 = %[fetch_src_r0_result] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r0;"
:
: __imm_addr(fetch_src_r0_value),
@@ -242,16 +242,16 @@ int fetch_r0(const void *ctx)
asm volatile (
"r0 = %[fetch_dst_r0_value] ll;"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = 0x1100000011 ll;"
".8byte %[fetch_dst_r0_insn];"
"r2 = %[fetch_dst_r0_result] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r1;"
/* r0 is only read by the atomic, so it must still be the pointer. */
"r3 = *(u64 *)(r0 + 0);"
"r2 = %[fetch_dst_r0_readback] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r3;"
:
: __imm_addr(fetch_dst_r0_value),
@@ -363,17 +363,17 @@ int uaf(const void *ctx)
asm volatile (
"r0 = %[page];"
"r0 += 4096;"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = 1;"
"r2 = %[uaf_r0_before_dst_r0] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r0;"
".8byte %[fetch_dst_r0];"
"r2 = %[uaf_r0_after_dst_r0] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r0;"
"r2 = %[uaf_r1_after_dst_r0] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r1;"
: : [page]"r"(page64),
__imm_insn(fetch_dst_r0,
@@ -387,11 +387,11 @@ int uaf(const void *ctx)
asm volatile (
"r1 = %[page];"
"r1 += 4096;"
- "r1 = addr_space_cast(r1, 0x0, 0x1);"
+ "r1 = addr_space_cast(r1, 0, 1);"
"r0 = 1;"
".8byte %[fetch_src_r0];"
"r2 = %[uaf_r0_after_src_r0] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r0;"
: : [page]"r"(page64),
__imm_insn(fetch_src_r0,
@@ -409,10 +409,10 @@ int uaf(const void *ctx)
asm volatile (
"r1 = %[page];"
"r1 += 4096;"
- "r1 = addr_space_cast(r1, 0x0, 0x1);"
+ "r1 = addr_space_cast(r1, 0, 1);"
"r3 = *(u64 *)(r1 + 0);"
"r2 = %[uaf_reread] ll;"
- "r2 = addr_space_cast(r2, 0x0, 0x1);"
+ "r2 = addr_space_cast(r2, 0, 1);"
"*(u64 *)(r2 + 0) = r3;"
: : [page]"r"(page64),
__imm_addr(uaf_reread)
@@ -460,10 +460,10 @@ int load_acquire(const void *ctx)
#define LOAD_ACQUIRE_ARENA(SIZEOP, SIZE, SRC, DST) \
{ asm volatile ( \
"r1 = %[" #SRC "] ll;" \
- "r1 = addr_space_cast(r1, 0x0, 0x1);" \
+ "r1 = addr_space_cast(r1, 0, 1);" \
".8byte %[load_acquire_insn];" \
"r3 = %[" #DST "] ll;" \
- "r3 = addr_space_cast(r3, 0x0, 0x1);" \
+ "r3 = addr_space_cast(r3, 0, 1);" \
"*(" #SIZE " *)(r3 + 0) = r2;" \
: \
: __imm_addr(SRC), \
@@ -515,7 +515,7 @@ int store_release(const void *ctx)
{ asm volatile ( \
"r1 = " VAL ";" \
"r2 = %[" #DST "] ll;" \
- "r2 = addr_space_cast(r2, 0x0, 0x1);" \
+ "r2 = addr_space_cast(r2, 0, 1);" \
".8byte %[store_release_insn];" \
: \
: __imm_addr(DST), \
diff --git a/tools/testing/selftests/bpf/progs/jit_insns_arena.c b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
index 95736eae2a58..997a897d66eb 100644
--- a/tools/testing/selftests/bpf/progs/jit_insns_arena.c
+++ b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
@@ -47,7 +47,7 @@ int arena_zero_off(void *ctx)
/* Accesses through two base registers */
asm volatile (" \
r7 = %[res]; \
- r7 = addr_space_cast(r7, 0x0, 0x1); \
+ r7 = addr_space_cast(r7, 0, 1); \
r1 = r7; \
r8 = 0x1122334455667788 ll; \
*(u64 *)(r7 + 0) = r8; \
@@ -93,12 +93,12 @@ int cast_to_user(void *ctx)
/* The user address comes back from the kernel one, NULL stays NULL */
asm volatile (" \
r7 = %[page]; \
- r1 = addr_space_cast(r7, 0x0, 0x1); \
- r1 = addr_space_cast(r1, 0x1, 0x0); \
+ r1 = addr_space_cast(r7, 0, 1); \
+ r1 = addr_space_cast(r1, 1, 0); \
r9 = r1; \
r9 ^= r7; \
r2 = 0; \
- r2 = addr_space_cast(r2, 0x1, 0x0); \
+ r2 = addr_space_cast(r2, 1, 0); \
r9 |= r2; \
%[res] = r9; \
" : [res]"=r"(res)
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index b17a6dfc100d..9b149c724160 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -524,7 +524,7 @@ int scalar_add_arena_ptr(void *ctx)
asm volatile (
"%[arena_ptr] = 8192;"
- "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0x0, 0x1);"
+ "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0, 1);"
"%[scalar] = 12;"
"%[scalar] += %[arena_ptr];"
: [scalar] "=r"(scalar),
@@ -549,9 +549,9 @@ int arena_ptr_add_arena_ptr(void *ctx)
asm volatile (
"%[arena_ptr1] = 8192;"
- "%[arena_ptr1] = addr_space_cast(%[arena_ptr1], 0x0, 0x1);"
+ "%[arena_ptr1] = addr_space_cast(%[arena_ptr1], 0, 1);"
"%[arena_ptr2] = 4096;"
- "%[arena_ptr2] = addr_space_cast(%[arena_ptr2], 0x0, 0x1);"
+ "%[arena_ptr2] = addr_space_cast(%[arena_ptr2], 0, 1);"
"%[arena_ptr2] += %[arena_ptr1];"
: [arena_ptr2] "=r"(arena_ptr2),
[arena_ptr1] "=&r"(arena_ptr1)
@@ -574,7 +574,7 @@ int mixed_arena_scalar_alu64_scalar_first(void *ctx)
asm volatile (
"r9 = %[arena] ll;"
"%[reg] = 0;"
- "%[reg] = addr_space_cast(%[reg], 0x0, 0x1);"
+ "%[reg] = addr_space_cast(%[reg], 0, 1);"
: [reg] "=r"(reg)
: __imm_addr(arena)
: "r9"
@@ -596,7 +596,7 @@ int mixed_arena_scalar_alu64_arena_first(void *ctx)
asm volatile (
"r9 = %[arena] ll;"
"%[reg] = 0;"
- "%[reg] = addr_space_cast(%[reg], 0x0, 0x1);"
+ "%[reg] = addr_space_cast(%[reg], 0, 1);"
: [reg] "=r"(reg)
: __imm_addr(arena)
: "r9"
@@ -635,7 +635,7 @@ int scalar_xor_arena_ptr(void *ctx)
asm volatile (
"%[arena_ptr] = 8192;"
- "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0x0, 0x1);"
+ "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0, 1);"
"%[scalar] = 12;"
"%[scalar] ^= %[arena_ptr];"
: [scalar] "=r"(scalar),
@@ -660,7 +660,7 @@ int arena_ptr_add_to_non_arena_ptr(void *ctx)
asm volatile (
"%[arena_ptr] = 8192;"
- "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0x0, 0x1);"
+ "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0, 1);"
"%[dst] = %[ctx];"
"%[dst] += %[arena_ptr];"
: [arena_ptr] "=&r"(arena_ptr),
@@ -685,7 +685,7 @@ int non_arena_ptr_add_to_arena_ptr(void *ctx)
asm volatile (
"%[arena_ptr] = 8192;"
- "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0x0, 0x1);"
+ "%[arena_ptr] = addr_space_cast(%[arena_ptr], 0, 1);"
"%[src] = %[ctx];"
"%[arena_ptr] += %[src];"
: [arena_ptr] "=&r"(arena_ptr),
diff --git a/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c b/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
index a8709ec447aa..b94892fff2f8 100644
--- a/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
@@ -29,7 +29,7 @@ __naked void uhmul64_arena_ptr(void)
asm volatile (
"r1 = %[arena] ll;"
"r0 = 1;"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
".8byte %[hmul];"
"exit;"
:
@@ -48,7 +48,7 @@ __naked void shmul64_arena_ptr(void)
asm volatile (
"r1 = %[arena] ll;"
"r1 = 1;"
- "r1 = addr_space_cast(r1, 0x0, 0x1);"
+ "r1 = addr_space_cast(r1, 0, 1);"
"r0 = 2;"
".8byte %[hmul];"
"exit;"
diff --git a/tools/testing/selftests/bpf/progs/verifier_ldsx.c b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
index 49e8cd4bb4f9..45b2e52fd715 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ldsx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
@@ -310,7 +310,7 @@ __naked void arena_ldsx_disasm(void *ctx)
"r4 = %[numa_no_node];"
"r5 = 0;"
"call %[bpf_arena_alloc_pages];"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = r0;"
"r8 = *(s32 *)(r0 + 16);"
"r8 = *(s16 *)(r0 + 24);"
@@ -338,7 +338,7 @@ __naked void arena_ldsx_exception(void *ctx)
asm volatile (
"r1 = %[arena] ll;"
"r0 = 0xdeadbeef;"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = 0x3fe;"
"*(u64 *)(r0 + 0) = r1;"
"r0 = *(s8 *)(r0 + 0);"
@@ -364,7 +364,7 @@ __naked void arena_ldsx_s8(void *ctx)
"r4 = %[numa_no_node];"
"r5 = 0;"
"call %[bpf_arena_alloc_pages];"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = 0x3fe;"
"*(u64 *)(r0 + 0) = r1;"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -396,7 +396,7 @@ __naked void arena_ldsx_s16(void *ctx)
"r4 = %[numa_no_node];"
"r5 = 0;"
"call %[bpf_arena_alloc_pages];"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = 0x3fffe;"
"*(u64 *)(r0 + 0) = r1;"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
@@ -428,7 +428,7 @@ __naked void arena_ldsx_s32(void *ctx)
"r4 = %[numa_no_node];"
"r5 = 0;"
"call %[bpf_arena_alloc_pages];"
- "r0 = addr_space_cast(r0, 0x0, 0x1);"
+ "r0 = addr_space_cast(r0, 0, 1);"
"r1 = 0xfffffffe;"
"*(u64 *)(r0 + 0) = r1;"
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 4/8] selftests/bpf: spell the addr_space_cast address spaces in decimal
2026-11-03 18:18 ` [PATCH bpf-next v4 4/8] selftests/bpf: spell the addr_space_cast address spaces in decimal Vineet Gupta
@ 2026-11-03 18:57 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-11-03 18:57 UTC (permalink / raw)
To: vineet.gupta, 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, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2406 bytes --]
> selftests/bpf: spell the addr_space_cast address spaces in decimal
TL;DR: Commit message only: the last sentence about GCC and "capable binutils"
is unclear, since the edited code is still behind
__BPF_FEATURE_ADDR_SPACE_CAST. Suggests saying it prepares for the later
guard-dropping patch.
> selftests/bpf: spell the addr_space_cast address spaces in decimal
>
> The inline asm in the arena tests writes the two address spaces of
> addr_space_cast() in hex which trips up a GNU assembler bug:
>
> progs/jit_insns_arena.c:49: Error: unrecognized instruction `r7 =addr_space_cast(r7,0x0,0x1)'
> progs/jit_insns_arena.c:49: Error: expected ',', got 'x'
>
> This has been fixed and landed binutils commit 835aee81fa93 ("bpf: accept
> any spelling of the addr_space_cast address spaces"), but will only land
> in the 2.47 release, so add the workarounds for now.
> A GCC that defines __BPF_FEATURE_ADDR_SPACE_CAST would be paired with
> a capable binutils.
>
> No functional change.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
This isn't a bug, but the last sentence of the changelog is unclear:
"A GCC that defines __BPF_FEATURE_ADDR_SPACE_CAST would be paired with
a capable binutils."
Every addr_space_cast() edited in this patch sits inside an
#if defined(__BPF_FEATURE_ADDR_SPACE_CAST) block (jit_insns_arena.c,
verifier_arena.c, verifier_hmul_arena.c, and the guards in fetch_r0(),
uaf(), load_acquire() and store_release() in arena_atomics.c).
Read literally, that means none of the edited code can reach the buggy
assembler at this point. The gas failure quoted for jit_insns_arena.c
only becomes reachable once the later commit 05738ae8e1ca ("selftests/bpf:
drop the arena guards and tag the tests that need arena") removes those
guards.
Since every addr_space_cast changed here is still behind
__BPF_FEATURE_ADDR_SPACE_CAST, which bpf-gcc does not define yet, could
the changelog say this prepares for the following patch that drops those
guards, and say what "capable binutils" means here (one that knows the
addr_space_cast mnemonic, or one that also accepts the hex spelling)?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41694978246
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 5/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
` (3 preceding siblings ...)
2026-11-03 18:18 ` [PATCH bpf-next v4 4/8] selftests/bpf: spell the addr_space_cast address spaces in decimal Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:57 ` bot+bpf-ci
2026-11-03 18:18 ` [PATCH bpf-next v4 6/8] selftests/bpf: drop the arena guards and tag the tests that need arena Vineet Gupta
` (3 subsequent siblings)
8 siblings, 1 reply; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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");
and 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
And while here also bring in __load_if_JITed(). 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.
Both tests become visible once 6/8 stops hiding them, and pass:
verifier_arena/mixed_arena_scalar_alu64_scalar_first -> OK
verifier_arena/mixed_arena_scalar_alu64_arena_first -> OK
Notes:
- This does need an __arena capable BPF_GCC.
- Under clang the rewrite is not a no-op: the existing version 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>
---
v3 -> v4: was 7/8, now ahead of the guard removal that stops hiding
these tests. Otherwise unchanged.
.../selftests/bpf/progs/verifier_arena.c | 91 +++++++++++--------
1 file changed, 51 insertions(+), 40 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index 9b149c724160..146febd99523 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -561,53 +561,64 @@ int arena_ptr_add_arena_ptr(void *ctx)
return 0;
}
+/*
+ * Tests that one insn cannot be reached with a register holding a scalar
+ * on one path and an arena pointer on the other.
+ */
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], 0, 1);"
- : [reg] "=r"(reg)
- : __imm_addr(arena)
- : "r9"
- );
- }
-
- reg += 1;
-
- return 0;
+__load_if_JITed()
+__naked void mixed_arena_scalar_alu64_scalar_first(void)
+{
+ /*
+ * The r9 load is never read. libbpf binds the object's arena to
+ * every program through fd_array, so the verifier knows about it
+ * without this; the load is the fallback for a kernel without
+ * fd_array_cnt and for the light skeleton loader.
+ */
+ 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);
}
+/*
+ * As mixed_arena_scalar_alu64_scalar_first(), including the r9 load, with
+ * the arena pointer established first.
+ */
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], 0, 1);"
- : [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 [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 5/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
2026-11-03 18:18 ` [PATCH bpf-next v4 5/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
@ 2026-11-03 18:57 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-11-03 18:57 UTC (permalink / raw)
To: vineet.gupta, 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, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3260 bytes --]
> selftests/bpf: write the mixed arena/scalar ALU tests in asm
TL;DR: Commit message only, no code issues: it refers to a follow-up patch as
"6/8" and says an arena-capable GCC is needed; both should name the commit by
subject, since series numbers vanish from git history.
> selftests/bpf: write the mixed arena/scalar ALU tests in asm
>
> 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");
>
> and 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
>
> And while here also bring in __load_if_JITed(). 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.
>
> Both tests become visible once 6/8 stops hiding them, and pass:
>
> verifier_arena/mixed_arena_scalar_alu64_scalar_first -> OK
> verifier_arena/mixed_arena_scalar_alu64_arena_first -> OK
>
> Notes:
> - This does need an __arena capable BPF_GCC.
> - Under clang the rewrite is not a no-op: the existing version 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>
>
this isn't a bug, but could the changelog name the follow-up commit by its
subject instead of "6/8"? The series number is not kept once the patches
are applied, so the reference no longer identifies anything in git history.
The same applies to the "Notes:" line "This does need an __arena capable
BPF_GCC", which only holds once that later commit has dropped the
__BPF_FEATURE_ADDR_SPACE_CAST guard around these tests.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41694978246
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 6/8] selftests/bpf: drop the arena guards and tag the tests that need arena
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
` (4 preceding siblings ...)
2026-11-03 18:18 ` [PATCH bpf-next v4 5/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:57 ` bot+bpf-ci
2026-11-03 18:18 ` [PATCH bpf-next v4 7/8] selftests/bpf: cast explicitly in the struct_ops arena triggers Vineet Gupta
` (2 subsequent siblings)
8 siblings, 1 reply; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
Arena tests are gated behind __BPF_FEATURE_ADDR_SPACE_CAST inside the
function body which is not ideal. For BPF_GCC which currently lacks
arena support, it leave an empty function which returns success (0)
giving the false impression that it is implemented and works.
#626/9 verifier_arena/basic_alloc1:OK
Turns out depite the lack of support most arena tests can be built and
work too. So drop the guards and let the programs be built.
Add __skip_without_arena for the few that do need the feature and will
be marked SKIP.
Measured on bpf-next with a stock bpf-gcc, test_progs before and
after. 20 subtests that were invisible now run and pass, five more now
say why they do not, and 13 empty bodies stop claiming to pass. One of
each:
verifier_ldsx/Arena LDSX, S8 -> OK
verifier_arena_large/big_alloc2 -> SKIP
verifier_arena/basic_alloc1 OK -> SKIP
verifier_hmul_arena.c is also included in the sweep as it used the old
pattern of using __skip() and changing this makes the usage consistent.
verifier_hmul_arena SKIP -> OK
Approach suggested by Alexei Starovoitov.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: replaces 3/8 and 5/8.
The #else dummies pattern removed (Alexei)
The guards go and the tests that need arena are tagged instead.
.../testing/selftests/bpf/progs/arena_kfunc.c | 18 +++-------
.../selftests/bpf/progs/arena_kfunc_jit.c | 5 +--
tools/testing/selftests/bpf/progs/bpf_misc.h | 9 +++++
.../bpf/progs/compute_live_registers.c | 2 --
.../selftests/bpf/progs/jit_insns_arena.c | 2 --
.../selftests/bpf/progs/verifier_arena.c | 35 +++----------------
.../bpf/progs/verifier_arena_globals1.c | 6 ++--
.../bpf/progs/verifier_arena_globals2.c | 3 +-
.../bpf/progs/verifier_arena_large.c | 21 +++--------
.../selftests/bpf/progs/verifier_hmul_arena.c | 15 --------
.../selftests/bpf/progs/verifier_ldsx.c | 4 ---
.../selftests/bpf/progs/verifier_zext.c | 4 ---
12 files changed, 27 insertions(+), 97 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/arena_kfunc.c b/tools/testing/selftests/bpf/progs/arena_kfunc.c
index 5d1cb1a7e8ee..757aa0618571 100644
--- a/tools/testing/selftests/bpf/progs/arena_kfunc.c
+++ b/tools/testing/selftests/bpf/progs/arena_kfunc.c
@@ -29,9 +29,9 @@ SEC("syscall")
__arch_x86_64
__arch_arm64
__success __retval(0)
+__skip_without_arena
int arena_arg_forms(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
u64 ret;
@@ -59,7 +59,6 @@ int arena_arg_forms(void *ctx)
return 4;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
@@ -75,7 +74,6 @@ __arch_arm64
__success __retval(0)
int arena_arg_rebase(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
u64 base, off;
@@ -107,7 +105,6 @@ int arena_arg_rebase(void *ctx)
return 7;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
@@ -115,9 +112,9 @@ SEC("syscall")
__arch_x86_64
__arch_arm64
__success __retval(0)
+__skip_without_arena
int arena_args5(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
@@ -139,7 +136,6 @@ int arena_args5(void *ctx)
return 3;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
@@ -147,9 +143,9 @@ SEC("syscall")
__arch_x86_64
__arch_arm64
__success __retval(0)
+__skip_without_arena
int arena_arg_mixed(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
@@ -166,7 +162,6 @@ int arena_arg_mixed(void *ctx)
return 3;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
@@ -177,7 +172,6 @@ __arch_arm64
__success __retval(0)
int arena_arg_unpopulated(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
@@ -188,7 +182,6 @@ int arena_arg_unpopulated(void *ctx)
bpf_kfunc_arena_arg_test((u64 *)stash);
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
@@ -220,9 +213,9 @@ SEC("syscall")
__arch_x86_64
__arch_arm64
__success __retval(0)
+__skip_without_arena
int arena_arg_subprog(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
int ret;
@@ -238,9 +231,6 @@ int arena_arg_subprog(void *ctx)
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
return ret;
-#else
- return 0;
-#endif
}
SEC("syscall")
diff --git a/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c b/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
index b5a01cbc33a7..16c3dd93ed98 100644
--- a/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
+++ b/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
@@ -23,8 +23,6 @@ struct {
/* volatile to force the scalar reloads below */
volatile u64 stash;
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
-
SEC("syscall")
__arch_x86_64
__jited("...")
@@ -93,6 +91,7 @@ __jited(" cbz w4, L0")
__jited(" add x4, x28, w4, uxtw")
__jited("L0: {{.*}}")
__success
+__skip_without_arena
int arena_arg_jit_args5(void *ctx)
{
u64 __arena *val;
@@ -113,6 +112,4 @@ int arena_arg_jit_args5(void *ctx)
return 0;
}
-#endif /* __BPF_FEATURE_ADDR_SPACE_CAST */
-
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index 1b264480e128..6b930cb48f8d 100644
--- a/tools/testing/selftests/bpf/progs/bpf_misc.h
+++ b/tools/testing/selftests/bpf/progs/bpf_misc.h
@@ -116,6 +116,10 @@
* feature the toolchain or JIT cannot provide, so a run does
* not report OK for coverage it never executed.
*
+ * __skip_without_arena
+ * __skip() when the compiler does not support the arena
+ * address space, nothing otherwise.
+ *
* __log_level Log level to use for the program, numeric value expected.
*
* __flag Adds one flag use for the program, the following values are valid:
@@ -156,6 +160,11 @@
#define __success __test_tag("test_expect_success")
#define __description(desc) __test_tag("test_description=" desc)
#define __skip(reason) __test_tag("test_skip=" reason)
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+#define __skip_without_arena
+#else
+#define __skip_without_arena __skip("compiler does not support the arena address space")
+#endif
#define __msg_unpriv(msg) __test_tag("test_expect_msg_unpriv=" msg)
#define __not_msg_unpriv(msg) __test_tag("test_expect_not_msg_unpriv=" msg)
#define __msg_next_unpriv(msg) __test_tag("test_expect_msg_next_unpriv=" msg)
diff --git a/tools/testing/selftests/bpf/progs/compute_live_registers.c b/tools/testing/selftests/bpf/progs/compute_live_registers.c
index 1ff24bbc7beb..a54f68bcd2ba 100644
--- a/tools/testing/selftests/bpf/progs/compute_live_registers.c
+++ b/tools/testing/selftests/bpf/progs/compute_live_registers.c
@@ -378,7 +378,6 @@ __naked void ldabs(void)
}
-#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__log_level(2)
__msg(" 6: .12345.... (85) call bpf_arena_alloc_pages")
@@ -403,7 +402,6 @@ __naked void addr_space_cast(void)
__imm_addr(arena)
: __clobber_all);
}
-#endif
static __used __naked int aux1(void)
{
diff --git a/tools/testing/selftests/bpf/progs/jit_insns_arena.c b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
index 997a897d66eb..ee591f0dcf95 100644
--- a/tools/testing/selftests/bpf/progs/jit_insns_arena.c
+++ b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
@@ -32,7 +32,6 @@ struct {
#define VAL_SUM 0x11224578aaccef10
#endif
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
SEC("syscall")
__success __retval(0)
int arena_zero_off(void *ctx)
@@ -106,6 +105,5 @@ int cast_to_user(void *ctx)
: "r1", "r2", "r7", "r9");
return res != 0;
}
-#endif
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index 146febd99523..cadb13f023d2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -27,9 +27,9 @@ struct {
SEC("socket")
__success __retval(0)
+__skip_without_arena
int basic_alloc1_nosleep(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile int __arena *page1, *page2, *no_page;
page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
@@ -52,15 +52,14 @@ int basic_alloc1_nosleep(void *ctx)
return 6;
if (*page2 != 0 && *page2 != 2) /* use-after-free should return 0 or the stored value */
return 7;
-#endif
return 0;
}
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int basic_alloc1(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile int __arena *page1, *page2, *no_page, *page3;
page1 = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
@@ -91,7 +90,6 @@ int basic_alloc1(void *ctx)
return 9;
if (*page1 != 1)
return 10;
-#endif
return 0;
}
@@ -125,9 +123,9 @@ int free_scalar_below_arena(void *ctx)
SEC("socket")
__success __retval(0)
+__skip_without_arena
int basic_alloc2_nosleep(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile char __arena *page1, *page2, *page3, *page4;
page1 = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);
@@ -157,15 +155,14 @@ int basic_alloc2_nosleep(void *ctx)
return 7;
if (*page4 != 0)
return 8;
-#endif
return 0;
}
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int basic_alloc2(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile char __arena *page1, *page2, *page3, *page4;
page1 = bpf_arena_alloc_pages(&arena, NULL, 2, NUMA_NO_NODE, 0);
@@ -195,7 +192,6 @@ int basic_alloc2(void *ctx)
return 7;
if (*page4 != 0)
return 8;
-#endif
return 0;
}
@@ -233,7 +229,6 @@ SEC("socket")
__success __retval(0)
int basic_reserve1_nosleep(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -257,7 +252,6 @@ int basic_reserve1_nosleep(void *ctx)
page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
if (page)
return 4;
-#endif
return 0;
}
@@ -265,7 +259,6 @@ SEC("syscall")
__success __retval(0)
int basic_reserve1(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -289,7 +282,6 @@ int basic_reserve1(void *ctx)
page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
if (page)
return 4;
-#endif
return 0;
}
@@ -297,7 +289,6 @@ SEC("socket")
__success __retval(0)
int basic_reserve2_nosleep(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -309,7 +300,6 @@ int basic_reserve2_nosleep(void *ctx)
page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0);
if ((u64)page)
return 2;
-#endif
return 0;
}
@@ -317,7 +307,6 @@ SEC("syscall")
__success __retval(0)
int basic_reserve2(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -329,7 +318,6 @@ int basic_reserve2(void *ctx)
page = bpf_arena_alloc_pages(&arena, page, 1, NUMA_NO_NODE, 0);
if ((u64)page)
return 2;
-#endif
return 0;
}
@@ -338,7 +326,6 @@ SEC("socket")
__success __retval(0)
int reserve_twice_nosleep(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -351,7 +338,6 @@ int reserve_twice_nosleep(void *ctx)
ret = bpf_arena_reserve_pages(&arena, page, 1);
if (ret != -EBUSY)
return 2;
-#endif
return 0;
}
@@ -359,7 +345,6 @@ SEC("syscall")
__success __retval(0)
int reserve_twice(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -372,7 +357,6 @@ int reserve_twice(void *ctx)
ret = bpf_arena_reserve_pages(&arena, page, 1);
if (ret != -EBUSY)
return 2;
-#endif
return 0;
}
@@ -381,7 +365,6 @@ SEC("socket")
__success __retval(0)
int reserve_invalid_region_nosleep(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -403,7 +386,6 @@ int reserve_invalid_region_nosleep(void *ctx)
ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1);
if (ret != -EINVAL)
return 4;
-#endif
return 0;
}
@@ -411,7 +393,6 @@ SEC("syscall")
__success __retval(0)
int reserve_invalid_region(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -433,7 +414,6 @@ int reserve_invalid_region(void *ctx)
ret = bpf_arena_reserve_pages(&arena, page, (1ULL << 32) - 1);
if (ret != -EINVAL)
return 4;
-#endif
return 0;
}
@@ -478,7 +458,6 @@ SEC("syscall")
__success __retval(0)
int arena_kfuncs_under_bpf_lock(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *page;
int ret;
@@ -503,13 +482,10 @@ int arena_kfuncs_under_bpf_lock(void *ctx)
bpf_arena_free_pages(&arena, page, 1);
bpf_spin_unlock(&arena_bpf_test_lock);
-#endif
return 0;
}
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
-
/*
* Test that scalar += PTR_TO_ARENA correctly upgrades the
* destination register to a PTR_TO_ARENA.
@@ -740,8 +716,6 @@ __naked void mixed_arena_stack_atomic(void)
: __clobber_all);
}
-#endif /* defined(__BPF_FEATURE_ADDR_SPACE_CAST) */
-
static __noinline
u32 __arena *check_arena_arg_nonglobal(u32 __arena *arg)
{
@@ -782,6 +756,7 @@ u32 __arena volatile *check_arena_arg_quals2(u32 volatile __arena *arg1, u32 __a
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int check_arena_arg_ret(void *ctx)
{
u32 __arena *page = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena_globals1.c b/tools/testing/selftests/bpf/progs/verifier_arena_globals1.c
index 45d364b0bc85..31be4c944252 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena_globals1.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena_globals1.c
@@ -30,9 +30,9 @@ volatile char __arena global_data[GLOBAL_PAGES][PAGE_SIZE];
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int check_reserve1(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
const u8 magic = 0x5a;
__u8 __arena *guard, *globals;
volatile char __arena *ptr;
@@ -60,7 +60,6 @@ int check_reserve1(void *ctx)
if (*ptr != magic)
return i + 3;
}
-#endif
return 0;
}
@@ -69,9 +68,9 @@ int check_reserve1(void *ctx)
*/
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int check_relocation(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
const u8 magic = 0xfa;
u8 __arena *ptr;
@@ -80,7 +79,6 @@ int check_relocation(void *ctx)
if (*ptr != magic)
return 1;
-#endif
return 0;
}
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena_globals2.c b/tools/testing/selftests/bpf/progs/verifier_arena_globals2.c
index b51594dbc005..cd48d6bd782e 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena_globals2.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena_globals2.c
@@ -30,9 +30,9 @@ char __arena global_data[ARENA_PAGES][PAGE_SIZE];
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int check_reserve2(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
void __arena *guard;
int ret;
@@ -42,7 +42,6 @@ int check_reserve2(void *ctx)
ret = bpf_arena_reserve_pages(&arena, guard, 1);
if (!ret)
return 1;
-#endif
return 0;
}
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena_large.c b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
index dbdea14ca76f..89f32a7772ac 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena_large.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
@@ -22,9 +22,9 @@ struct {
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int big_alloc1(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile char __arena *page1, *page2, *no_page, *page3;
u64 base;
@@ -79,16 +79,15 @@ int big_alloc1(void *ctx)
return 13;
if (*(page2 - PAGE_SIZE) != 0)
return 14;
-#endif
return 0;
}
/* Try to access a reserved page. Behavior should be identical with accessing unallocated pages. */
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int access_reserved(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile char __arena *page;
char __arena *base;
const size_t len = 4;
@@ -118,7 +117,6 @@ int access_reserved(void *ctx)
if (*page)
return 2 + 2 * i + 1;
}
-#endif
return 0;
}
@@ -127,7 +125,6 @@ SEC("syscall")
__success __retval(0)
int request_partially_reserved(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
volatile char __arena *page;
char __arena *base;
int ret;
@@ -142,7 +139,6 @@ int request_partially_reserved(void *ctx)
page = bpf_arena_alloc_pages(&arena, base, 5, NUMA_NO_NODE, 0);
if ((u64)page != 0ULL)
return 2;
-#endif
return 0;
}
@@ -150,7 +146,6 @@ SEC("syscall")
__success __retval(0)
int free_reserved(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *addr;
char __arena *page;
int ret;
@@ -177,11 +172,9 @@ int free_reserved(void *ctx)
page = bpf_arena_alloc_pages(&arena, addr + __PAGE_SIZE, 2, NUMA_NO_NODE, 0);
if (!page)
return 3;
-#endif
return 0;
}
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
#define PAGE_CNT 100
__u8 __arena * __arena page[PAGE_CNT]; /* occupies the first page */
__u8 __arena *base;
@@ -220,6 +213,7 @@ __noinline int alloc_pages(int page_cnt, int pages_atonce, bool first_pass,
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int big_alloc2(void *ctx)
{
__u8 __arena *pg;
@@ -290,9 +284,9 @@ int big_alloc2(void *ctx)
/* Nonsleepable because it binds to a socket program. */
SEC("socket")
__success __retval(0)
+__skip_without_arena
int big_alloc3(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *pages;
u64 i;
int err = 0;
@@ -314,16 +308,14 @@ int big_alloc3(void *ctx)
bpf_arena_free_pages(&arena, pages, LARGE_PAGE_CNT);
return err;
-#endif
- return 0;
}
/* SYSCALL programs are always sleepable. */
SEC("syscall")
__success __retval(0)
+__skip_without_arena
int big_alloc4(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *pages;
u64 i;
int err = 0;
@@ -345,8 +337,5 @@ int big_alloc4(void *ctx)
bpf_arena_free_pages(&arena, pages, LARGE_PAGE_CNT);
return err;
-#endif
- return 0;
}
-#endif
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c b/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
index b94892fff2f8..b4bcfb6b9646 100644
--- a/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
@@ -5,8 +5,6 @@
#include "bpf_misc.h"
#include "../../../include/linux/filter.h"
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
-
#define BPF_UHMUL64_IMM(DST, IMM) \
BPF_RAW_INSN(BPF_ALU64 | BPF_MUL | BPF_K, DST, 0, BPF_UHMUL, IMM)
@@ -58,17 +56,4 @@ __naked void shmul64_arena_ptr(void)
: __clobber_all);
}
-#else
-
-SEC("socket")
-__description("HMUL arena tests require compiler support for addr_space_cast")
-__skip("compiler doesn't support addr_space_cast")
-__success
-int dummy_test(void)
-{
- return 0;
-}
-
-#endif
-
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_ldsx.c b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
index 45b2e52fd715..8084a428d9e2 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ldsx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
@@ -257,8 +257,6 @@ __naked void ldsx_ctx_8(void)
: __clobber_all);
}
-#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
-
struct {
__uint(type, BPF_MAP_TYPE_ARENA);
__uint(map_flags, BPF_F_MMAPABLE);
@@ -451,8 +449,6 @@ void kfunc_root(void)
bpf_arena_alloc_pages(0, 0, 0, 0, 0);
}
-#endif /* __BPF_FEATURE_ADDR_SPACE_CAST */
-
#else
SEC("socket")
diff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c
index 572017fe28fb..9fce1dd8ef7b 100644
--- a/tools/testing/selftests/bpf/progs/verifier_zext.c
+++ b/tools/testing/selftests/bpf/progs/verifier_zext.c
@@ -324,8 +324,6 @@ __naked void alu64_and_conditionals(void)
: __clobber_all);
}
-#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
-
SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
__arch_s390x
__xlated("7: w1 = w0")
@@ -382,8 +380,6 @@ __naked void zext_arena_cmpxchg32(void)
: __clobber_all);
}
-#endif
-
/* Check if probe mem loads keep their zero extension. */
SEC("socket")
__success __log_level(2)
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 6/8] selftests/bpf: drop the arena guards and tag the tests that need arena
2026-11-03 18:18 ` [PATCH bpf-next v4 6/8] selftests/bpf: drop the arena guards and tag the tests that need arena Vineet Gupta
@ 2026-11-03 18:57 ` bot+bpf-ci
0 siblings, 0 replies; 13+ messages in thread
From: bot+bpf-ci @ 2026-11-03 18:57 UTC (permalink / raw)
To: vineet.gupta, 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, ast, andrii, daniel, martin.lau, eddyz87,
yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 6824 bytes --]
> selftests/bpf: drop the arena guards and tag the tests that need arena
TL;DR: Removing the addr_space_cast guards may break the selftest build with
clang 17/18, whose assembler lacks that mnemonic, so test_progs may fail to
build; keep a guard or emit raw bytes.
> diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
> --- a/tools/testing/selftests/bpf/progs/verifier_arena.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
[ ... ]
> @@ -503,13 +482,10 @@ int arena_kfuncs_under_bpf_lock(void *ctx)
> bpf_arena_free_pages(&arena, page, 1);
>
> bpf_spin_unlock(&arena_bpf_test_lock);
> -#endif
>
> return 0;
> }
>
> -#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
> -
> /*
> * Test that scalar += PTR_TO_ARENA correctly upgrades the
> * destination register to a PTR_TO_ARENA.
[ ... ]
> -#endif /* defined(__BPF_FEATURE_ADDR_SPACE_CAST) */
> -
Can this break the build with clang 17 and 18? The removed guard also
kept the textual addr_space_cast mnemonic away from assemblers that do not
know it, since that mnemonic and __BPF_FEATURE_ADDR_SPACE_CAST both
arrived in LLVM 19. Asm such as this is now always assembled:
asm volatile (
"%[arena_ptr] = 8192;"
"%[arena_ptr] = addr_space_cast(%[arena_ptr], 0, 1);"
...
in scalar_add_arena_ptr(), arena_ptr_add_arena_ptr(),
mixed_arena_scalar_alu64_scalar_first(),
mixed_arena_scalar_alu64_arena_first(), scalar_xor_arena_ptr(),
arena_ptr_add_to_non_arena_ptr(), non_arena_ptr_add_to_arena_ptr() and
mixed_arena_stack_atomic().
verifier_arena.skel.h is included by prog_tests/verifier.c, so test_progs
and its no_alu32 and cpuv4 flavors would fail to build, not just these
subtests. __skip_without_arena does not help since this fails at build
time.
Other places in the tree keep the guard for this reason, for example
arena_atomics.c and commit 9e35f216e5c5. Would a guard like
"defined(__BPF_FEATURE_ADDR_SPACE_CAST) || !defined(__clang__)", or
emitting the cast as raw bytes (as bpf_addr_space_cast() in
bpf_experimental.h does), keep both bpf-gcc and older clang working?
> diff --git a/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c b/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
> --- a/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_hmul_arena.c
> @@ -5,8 +5,6 @@
> #include "bpf_misc.h"
> #include "../../../include/linux/filter.h"
>
> -#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
> -
> #define BPF_UHMUL64_IMM(DST, IMM) \
> BPF_RAW_INSN(BPF_ALU64 | BPF_MUL | BPF_K, DST, 0, BPF_UHMUL, IMM)
>
[ ... ]
> -#else
> -
> -SEC("socket")
> -__description("HMUL arena tests require compiler support for addr_space_cast")
> -__skip("compiler doesn't support addr_space_cast")
> -__success
> -int dummy_test(void)
> -{
> - return 0;
> -}
> -
> -#endif
> -
> char _license[] SEC("license") = "GPL";
Can this break the build with clang 17 and 18? uhmul64_arena_ptr() and
shmul64_arena_ptr() contain:
"r0 = addr_space_cast(r0, 0, 1);"
"r1 = addr_space_cast(r1, 0, 1);"
The guard was about assembler support for that mnemonic (LLVM 19 and
later), not about compiler arena codegen. With clang 17 or 18 the
integrated assembler rejects these lines, so verifier_hmul_arena.bpf.o
fails to build, and test_progs with it. Previously those toolchains got
the dummy SKIP.
Commit ddd86412ac01 which added this file says "Retain
__BPF_FEATURE_ADDR_SPACE_CAST gating and a dummy skip program for
toolchains without addr_space_cast support."
Could the guard be kept in a form that both bpf-gcc and LLVM 19 or later
satisfy, for example "defined(__BPF_FEATURE_ADDR_SPACE_CAST) ||
!defined(__clang__)"? Or could the cast be emitted as raw bytes with
.8byte/__imm_insn(), the way the HMUL instruction itself is emitted here?
> diff --git a/tools/testing/selftests/bpf/progs/verifier_ldsx.c b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
> --- a/tools/testing/selftests/bpf/progs/verifier_ldsx.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
> @@ -257,8 +257,6 @@ __naked void ldsx_ctx_8(void)
> : __clobber_all);
> }
>
> -#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
> -
> struct {
> __uint(type, BPF_MAP_TYPE_ARENA);
> __uint(map_flags, BPF_F_MMAPABLE);
Can this break the build with clang 18? The outer guard of this file
accepts any clang 18 or later:
(__clang_major__ >= 18 || defined(__BPF_FEATURE_LDSX))
but clang 18 does not have the textual addr_space_cast mnemonic. The
inner __BPF_FEATURE_ADDR_SPACE_CAST check was what kept the Arena LDSX
programs away from it. Now arena_ldsx_disasm(), arena_ldsx_exception()
and arena_ldsx_s8/s16/s32() are assembled, and lines such as
"r0 = addr_space_cast(r0, 0, 1);"
are rejected, so verifier_ldsx.bpf.o fails to build. Would an inner
guard like "defined(__BPF_FEATURE_ADDR_SPACE_CAST) || !defined(__clang__)"
keep both working?
> diff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c
> --- a/tools/testing/selftests/bpf/progs/verifier_zext.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_zext.c
> @@ -324,8 +324,6 @@ __naked void alu64_and_conditionals(void)
> : __clobber_all);
> }
>
> -#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
> -
> SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
> __arch_s390x
> __xlated("7: w1 = w0")
[ ... ]
> -#endif
> -
> /* Check if probe mem loads keep their zero extension. */
Can this break the build with clang that lacks the addr_space_cast
mnemonic? arena_ptr() and zext_arena_cmpxchg32() now always contain:
r1 = addr_space_cast(r0, 0, 1);
r2 = addr_space_cast(r1, 1, 0);
r1 = addr_space_cast(r1, 0, 1);
With clang 18 or older the assembler rejects these, so verifier_zext.bpf.o
fails to build. That also takes the roughly twenty non-arena zext
subtests in the same object with it, and since prog_tests/verifier.c
includes verifier_zext.skel.h, test_progs fails to build.
__skip_without_arena cannot help because it is a runtime test_loader tag.
zext_arena_cmpxchg32() already emits its cmpxchg as .8byte through
__imm_insn(BPF_ATOMIC_OP(...)). Could the two casts be encoded the same
way, for example with __imm_insn(BPF_RAW_INSN(BPF_ALU64 | BPF_MOV |
BPF_X, dst, src, BPF_ADDR_SPACE_CAST, (dst_as << 16) | src_as))? The
alternative is to keep a compile-time guard for compilers that lack the
mnemonic.
The same pattern appears in the other files unguarded here:
jit_insns_arena.c and compute_live_registers.c.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/41694978246
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v4 7/8] selftests/bpf: cast explicitly in the struct_ops arena triggers
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
` (5 preceding siblings ...)
2026-11-03 18:18 ` [PATCH bpf-next v4 6/8] selftests/bpf: drop the arena guards and tag the tests that need arena Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 18:18 ` [PATCH bpf-next v4 8/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
2026-11-03 21:20 ` [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC patchwork-bot+netdevbpf
8 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
trigger() in struct_ops_arena.c and its three siblings puts the
__BPF_FEATURE_ADDR_SPACE_CAST guard inside the function, so without
arena support the body is gone and the program falls through to
"return 0". The host side then does
err = bpf_prog_test_run_opts(bpf_program__fd(trigger), &topts);
ASSERT_OK(err, "test_run");
ASSERT_EQ(topts.retval, 0, "trigger_retval");
and passes, having run a program that did nothing.
These are not driven by test_loader, so __skip_without_arena cannot
reach them. They do not need it: cast_kern() is a nop when the
compiler supports the address space and bpf_addr_space_cast(ptr, 0, 1)
when it does not, so one cast makes the body work either way and the
tests run for real instead of being skipped.
No verdict changes: struct_ops_arena/arena_arg and /arena_autobind
reported OK before, from an empty body, and report OK now from one
that runs. What changes is that the OK means something.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: was 8/8. cast_kern() instead of the skip_tests flag in
.data, so these run rather than skip. (Alexei)
tools/testing/selftests/bpf/progs/struct_ops_arena.c | 3 +--
tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c | 3 +--
tools/testing/selftests/bpf/progs/struct_ops_arena_member.c | 3 +--
.../selftests/bpf/progs/struct_ops_arena_member_render.c | 3 +--
4 files changed, 4 insertions(+), 8 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena.c b/tools/testing/selftests/bpf/progs/struct_ops_arena.c
index 8aa8639df91f..3fffa24839ee 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena.c
@@ -86,7 +86,6 @@ struct bpf_testmod_ops3 testmod_arena = {
SEC("syscall")
int trigger(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
int ret;
@@ -94,6 +93,7 @@ int trigger(void *ctx)
if (!val)
return 1;
+ cast_kern(val);
*val = 41;
ret = bpf_testmod_ops3_call_test_arena((u64 *)val);
if (ret)
@@ -134,6 +134,5 @@ int trigger(void *ctx)
return 11;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c b/tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c
index 495faa0f4a8b..3455ca9e2d47 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c
@@ -45,13 +45,13 @@ int unrelated(void *ctx)
SEC("syscall")
int trigger(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
if (!val)
return 1;
+ cast_kern(val);
*val = 41;
if (bpf_testmod_ops3_call_test_arena((u64 *)val))
return 2;
@@ -59,6 +59,5 @@ int trigger(void *ctx)
return 3;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena_member.c b/tools/testing/selftests/bpf/progs/struct_ops_arena_member.c
index a2218cd81619..afbe59f77af6 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_member.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_member.c
@@ -80,7 +80,6 @@ struct bpf_testmod_ops3 testmod_arena_member = {
SEC("syscall")
int trigger(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
int ret;
@@ -88,6 +87,7 @@ int trigger(void *ctx)
if (!val)
return 1;
+ cast_kern(val);
*val = 41;
ret = bpf_testmod_ops3_call_test_arena_member((u64 *)val);
if (ret)
@@ -104,6 +104,5 @@ int trigger(void *ctx)
return 4;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c b/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
index 09b4c740bdca..c4e134e14f87 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_member_render.c
@@ -63,7 +63,6 @@ struct bpf_testmod_ops3 testmod_arena_member_render = {
SEC("syscall")
int trigger(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
u64 __arena *val;
int ret;
@@ -71,6 +70,7 @@ int trigger(void *ctx)
if (!val)
return 1;
+ cast_kern(val);
*val = 41;
ret = bpf_testmod_ops3_call_test_arena_member((u64 *)val);
if (ret)
@@ -80,6 +80,5 @@ int trigger(void *ctx)
return 3;
bpf_arena_free_pages(&arena, (void __arena *)val, 1);
-#endif
return 0;
}
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v4 8/8] selftests/bpf: cast explicitly in the arena subprog and callback faults
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
` (6 preceding siblings ...)
2026-11-03 18:18 ` [PATCH bpf-next v4 7/8] selftests/bpf: cast explicitly in the struct_ops arena triggers Vineet Gupta
@ 2026-11-03 18:18 ` Vineet Gupta
2026-11-03 21:20 ` [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC patchwork-bot+netdevbpf
8 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-11-03 18:18 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
Stream tests fail with BPF_GCC, e.g.
test_stream_arena_fault_address:FAIL:stream__open_and_load unexpected error: -13
They call stream__open_and_load(), which loads every program in the object,
and two of those do not verify:
#481/11 stream_success/stream_arena_subprog_fault:FAIL
#481/12 stream_success/stream_arena_callback_fault:FAIL
Both dereference an arena pointer and leave the cast to the compiler:
int __arena *addr = (int __arena *)0xdeadbeef;
*addr = 1;
Without arena support __arena is only btf_type_tag("arena"), so no
addr_space_cast is emitted and the store is to a scalar, which the
verifier rejects. GCC splits it into byte stores, 0xdeadbeef not
being aligned:
r2=3735928559 ll
*(u8*)(r2+0)=1 <- plain store, no cast
Their siblings in the same file do not rely on the compiler; they use
bpf_addr_space_cast(), which emits the instruction byte by byte and
works anywhere. Do the same here.
The address space has to come off the local as well. Left on, a
compiler that supports arenas casts a second time for the
dereference, on a pointer the explicit cast has already converted:
r2 = addr_space_cast(r2, 0, 1)
r3 = addr_space_cast(r2, 0, 1)
*(u32*)(r3+0)=1
With it off, GCC with and without arena support and clang all emit the
same thing, which is what the test wants:
r2 = addr_space_cast(r2, 0, 1)
*(u32*)(r2+0)=1
What these two assert is the fault report, not how the pointer was
produced, so nothing they were testing is lost.
Measured on bpf-next with a stock bpf-gcc, test_progs before and
after. Every test that loads this object recovers, not just the arena
ones:
stream_arena_fault_address FAIL -> OK
stream_fd FAIL -> OK
stream_fd/blocking FAIL -> OK
stream_fd/edge FAIL -> OK
stream_fd/empty FAIL -> OK
stream_fd/hup FAIL -> OK
stream_fd/nmi_epoll FAIL -> OK
stream_fd/nonblock FAIL -> OK
stream_fd/open FAIL -> OK
stream_oversize FAIL -> OK
stream_partial_read FAIL -> OK
stream_success FAIL -> OK
stream_success/stream_arena_callback_fault FAIL -> OK
stream_success/stream_arena_subprog_fault FAIL -> OK
stream_syscall FAIL -> OK
and the subtests of the object that failed to load appear for the
first time:
stream_arena_fault_address/read_fault -> OK
stream_arena_fault_address/write_fault -> OK
stream_arena_fault_address/xchg_fault -> OK
stream_arena_fault_address/cmpxchg_fault -> OK
stream_arena_fault_address/load_acquire_fault -> OK
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
v3 -> v4: was 6/8. No longer drops #include <bpf_arena_common.h>,
which the arena_base() patch now needs in this file.
tools/testing/selftests/bpf/progs/stream.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/stream.c b/tools/testing/selftests/bpf/progs/stream.c
index b4d88d0e01ad..8c49a130150b 100644
--- a/tools/testing/selftests/bpf/progs/stream.c
+++ b/tools/testing/selftests/bpf/progs/stream.c
@@ -341,9 +341,10 @@ int stream_arena_cmpxchg_fault(void *ctx)
static __noinline void subprog(void)
{
- int __arena *addr = (int __arena *)0xdeadbeef;
+ int *addr = (int *)0xdeadbeef;
arena_ptr = &arena;
+ bpf_addr_space_cast(addr, 0, 1);
*addr = 1;
}
@@ -364,9 +365,10 @@ int stream_arena_subprog_fault(void *ctx)
static __noinline int timer_cb(void *map, int *key, struct bpf_timer *timer)
{
- int __arena *addr = (int __arena *)0xdeadbeef;
+ int *addr = (int *)0xdeadbeef;
arena_ptr = &arena;
+ bpf_addr_space_cast(addr, 0, 1);
*addr = 1;
return 0;
}
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC
2026-11-03 18:18 [PATCH bpf-next v4 0/8] selftests/bpf: let the arena tests build with GCC Vineet Gupta
` (7 preceding siblings ...)
2026-11-03 18:18 ` [PATCH bpf-next v4 8/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
@ 2026-11-03 21:20 ` patchwork-bot+netdevbpf
8 siblings, 0 replies; 13+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-11-03 21:20 UTC (permalink / raw)
To: Vineet Gupta
Cc: bpf, 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
Hello:
This series was applied to bpf/bpf-next.git (master)
by Alexei Starovoitov <ast@kernel.org>:
On Tue, 3 Nov 2026 10:18:08 -0800 you wrote:
> The arena selftests cannot be built by GCC today. Three separate
> build failures stand in the way, and they are all hidden rather than
> avoided by the __BPF_FEATURE_ADDR_SPACE_CAST guards wrapped around
> those tests -- the moment a GCC defines that macro, which is what the
> in-progress arena support does, the guards stop hiding them and the
> build breaks.
>
> [...]
Here is the summary with links:
- [bpf-next,v4,1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
https://git.kernel.org/bpf/bpf-next/c/5b127a2d9d44
- [bpf-next,v4,2/8] selftests/bpf: don't pass clang-only warning flags to GCC
https://git.kernel.org/bpf/bpf-next/c/a74a26a8f284
- [bpf-next,v4,3/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
https://git.kernel.org/bpf/bpf-next/c/2775ec48ecaa
- [bpf-next,v4,4/8] selftests/bpf: spell the addr_space_cast address spaces in decimal
https://git.kernel.org/bpf/bpf-next/c/0a2eb9a7fa34
- [bpf-next,v4,5/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
https://git.kernel.org/bpf/bpf-next/c/db06ab42ec4b
- [bpf-next,v4,6/8] selftests/bpf: drop the arena guards and tag the tests that need arena
https://git.kernel.org/bpf/bpf-next/c/84459cc976cd
- [bpf-next,v4,7/8] selftests/bpf: cast explicitly in the struct_ops arena triggers
https://git.kernel.org/bpf/bpf-next/c/7eead323843a
- [bpf-next,v4,8/8] selftests/bpf: cast explicitly in the arena subprog and callback faults
https://git.kernel.org/bpf/bpf-next/c/b64845f6280f
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 13+ messages in thread