* [PATCH bpf-next v2 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 21:22 ` bot+bpf-ci
2026-10-28 20:41 ` [PATCH bpf-next v2 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
` (6 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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 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: 76 subtests run that
previously did not exist in the report, 75 OK and one @unpriv SKIP,
with no failures. The two dummy placeholders retire and
callx_rodata_lskel goes SKIP -> OK.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
.../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 6cf58ca9ac5b..ab3e3b64616b 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
@@ -892,6 +890,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* Re: [PATCH bpf-next v2 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
2026-10-28 20:41 ` [PATCH bpf-next v2 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
@ 2026-10-28 21:22 ` bot+bpf-ci
2026-10-28 22:28 ` Vineet Gupta
0 siblings, 1 reply; 13+ messages in thread
From: bot+bpf-ci @ 2026-10-28 21:22 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: 3000 bytes --]
> selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
TL;DR:
- data_in_arena.c use_ops() isn't converted to CAN_USE_CALLX, so GCC with
callx support may still call add1() directly; commit message claims all
tests converted
- Commit message typo (nit)
> selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
>
> 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 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: 76 subtests run that
> previously did not exist in the report, 75 OK and one @unpriv SKIP,
> with no failures. The two dummy placeholders retire and
> callx_rodata_lskel goes SKIP -> OK.
>
> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
The commit message says CAN_USE_CALLX is used in all the test programs, but
use_ops() in progs/data_in_arena.c is not converted. It still gates its
indirect call on __clang__ and keeps the same comment this patch removes from
the other files:
progs/data_in_arena.c:use_ops() {
...
#ifdef __clang__
return ops.fn(*ops.data) + ops.name[1];
#else
/* gcc doesn't support indirect calls */
return add1(*ops.data) + ops.name[1];
#endif
...
}
With a GCC that defines __BPF_FEATURE_CALLX, doesn't this still take the
#else branch and call add1() directly? That means the GCC build never makes
the callx through a function pointer in .data.rel.ro, and the "gcc doesn't
support indirect calls" comment is now wrong for that compiler.
A grep for "indirect call" under progs/ and prog_tests/ shows this is the
only remaining gate. None of the later patches in the series touch
data_in_arena.c.
Should use_ops() be converted to CAN_USE_CALLX as well? That would need
data_in_arena.c to include bpf_misc.h, and it would add the x86/arm64/riscv64
arch gate that clang builds of this file don't have today. Otherwise, could
the commit message be adjusted so it doesn't say all the test programs were
converted?
This isn't a bug, but there is a missing word in the commit message:
> bpf_misc.h, and use it all the test programs.
This should read "use it in all the test programs".
---
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/40768193895
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v2 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
2026-10-28 21:22 ` bot+bpf-ci
@ 2026-10-28 22:28 ` Vineet Gupta
0 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 22:28 UTC (permalink / raw)
To: bot+bpf-ci, 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,
martin.lau, mason
10/28/26 2:22 PM, bot+bpf-ci@kernel.org wrote:
>> selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
> TL;DR:
> - data_in_arena.c use_ops() isn't converted to CAN_USE_CALLX, so GCC with
> callx support may still call add1() directly; commit message claims all
> tests converted
> - Commit message typo (nit)
>
>> selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
>>
>> 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 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: 76 subtests run that
>> previously did not exist in the report, 75 OK and one @unpriv SKIP,
>> with no failures. The two dummy placeholders retire and
>> callx_rodata_lskel goes SKIP -> OK.
>>
>> Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
> The commit message says CAN_USE_CALLX is used in all the test programs, but
> use_ops() in progs/data_in_arena.c is not converted. It still gates its
> indirect call on __clang__ and keeps the same comment this patch removes from
> the other files:
>
> progs/data_in_arena.c:use_ops() {
> ...
> #ifdef __clang__
> return ops.fn(*ops.data) + ops.name[1];
> #else
> /* gcc doesn't support indirect calls */
> return add1(*ops.data) + ops.name[1];
> #endif
> ...
> }
>
> With a GCC that defines __BPF_FEATURE_CALLX, doesn't this still take the
> #else branch and call add1() directly? That means the GCC build never makes
> the callx through a function pointer in .data.rel.ro, and the "gcc doesn't
> support indirect calls" comment is now wrong for that compiler.
>
> A grep for "indirect call" under progs/ and prog_tests/ shows this is the
> only remaining gate. None of the later patches in the series touch
> data_in_arena.c.
>
> Should use_ops() be converted to CAN_USE_CALLX as well? That would need
> data_in_arena.c to include bpf_misc.h, and it would add the x86/arm64/riscv64
> arch gate that clang builds of this file don't have today. Otherwise, could
> the commit message be adjusted so it doesn't say all the test programs were
> converted?
Yes it missed the grep. Will be fixed in v3.
Do note that this also adds the JIT gate which is the right thing to do
- the prior __clang__ only check would generate callx even for arches
not supporting callx.
> This isn't a bug, but there is a missing word in the commit message:
>> bpf_misc.h, and use it all the test programs.
> This should read "use it in all the test programs".
Right, will fix that as well.
Thx,
-Vineet
> ---
> 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/40768193895
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v2 2/8] selftests/bpf: don't pass clang-only warning flags to GCC
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build Vineet Gupta
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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 togges are passed to every BPF object build, including
for BPF_GCC build.
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>
---
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 1e4af62e8312..b5303c28973e 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.
@@ -521,7 +521,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
@@ -537,12 +537,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 ff3476bc40a4..8ae2792c0c1e 100644
--- a/tools/testing/selftests/bpf/Makefile.buildvars
+++ b/tools/testing/selftests/bpf/Makefile.buildvars
@@ -157,13 +157,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 v2 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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
Tests guarded by __BPF_FEATURE_ADDR_SPACE_CAST don't have a dummy fall back
when the compiler doesn't support the feature: so they are absent from such
a report entirely.
Every subtest inside one of those guards is invisible: 24 across seven
files, plus the five in stream_arena_fault_address, which has no guard
and fails to load instead (addressed separately, later in this series).
Give each guard an #else with a dummy marked __skip(), so the skip gap is
reported rather than invisible. Add __skip_without_arena for it, which
expands to __skip() with a fixed reason when the compiler cannot do arena.
Measured on bpf-next with a stock bpf-gcc, test_progs before and after:
seven subtests appear that were previously absent from the report
altogether, and arena_kfunc_jit goes OK -> SKIP because every subtest
it has is now one of them.
arena_kfunc_jit/arena_unsupported SKIP
compute_live_registers/arena_unsupported SKIP
jit_insns/arena_unsupported SKIP
verifier_arena/arena_unsupported SKIP
verifier_arena_large/arena_unsupported SKIP
verifier_ldsx/arena_unsupported SKIP
verifier_zext/arena_unsupported SKIP
jit_insns_arena.c needs the same treatment and is easy to miss: both
of its programs are inside the guard, so without arena support the
object has none at all and RUN_TESTS(jit_insns_arena) runs zero
subtests without saying so.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
.../testing/selftests/bpf/progs/arena_kfunc_jit.c | 9 +++++++++
tools/testing/selftests/bpf/progs/bpf_misc.h | 14 ++++++++++++++
.../selftests/bpf/progs/compute_live_registers.c | 10 ++++++++++
.../testing/selftests/bpf/progs/jit_insns_arena.c | 10 ++++++++++
tools/testing/selftests/bpf/progs/verifier_arena.c | 9 +++++++++
.../selftests/bpf/progs/verifier_arena_large.c | 10 ++++++++++
tools/testing/selftests/bpf/progs/verifier_ldsx.c | 9 +++++++++
tools/testing/selftests/bpf/progs/verifier_zext.c | 9 +++++++++
8 files changed, 80 insertions(+)
diff --git a/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c b/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
index b5a01cbc33a7..97912a7b6b5e 100644
--- a/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
+++ b/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
@@ -112,6 +112,15 @@ int arena_arg_jit_args5(void *ctx)
(u64 *)&val[4]);
return 0;
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(void)
+{
+ return 0;
+}
#endif /* __BPF_FEATURE_ADDR_SPACE_CAST */
diff --git a/tools/testing/selftests/bpf/progs/bpf_misc.h b/tools/testing/selftests/bpf/progs/bpf_misc.h
index 1b264480e128..ed1ed1b5bd09 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() with a fixed reason when the compiler cannot do
+ * 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,16 @@
#define __success __test_tag("test_expect_success")
#define __description(desc) __test_tag("test_description=" desc)
#define __skip(reason) __test_tag("test_skip=" reason)
+
+/*
+ * Without compiler arena support, report the test as SKIP rather than let it
+ * pass vacuously or fail to load.
+ */
+#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..e05431600358 100644
--- a/tools/testing/selftests/bpf/progs/compute_live_registers.c
+++ b/tools/testing/selftests/bpf/progs/compute_live_registers.c
@@ -403,6 +403,16 @@ __naked void addr_space_cast(void)
__imm_addr(arena)
: __clobber_all);
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(void)
+{
+ return 0;
+}
+
#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 95736eae2a58..ccda60e96524 100644
--- a/tools/testing/selftests/bpf/progs/jit_insns_arena.c
+++ b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
@@ -106,6 +106,16 @@ int cast_to_user(void *ctx)
: "r1", "r2", "r7", "r9");
return res != 0;
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(void)
+{
+ return 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 1ce9c4b78481..3b3410802825 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -713,6 +713,15 @@ __naked void mixed_arena_stack_atomic(void)
__imm(bpf_get_prandom_u32)
: __clobber_all);
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(void)
+{
+ return 0;
+}
#endif /* defined(__BPF_FEATURE_ADDR_SPACE_CAST) */
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena_large.c b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
index dbdea14ca76f..34f0a0f88e12 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena_large.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
@@ -348,5 +348,15 @@ int big_alloc4(void *ctx)
#endif
return 0;
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(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 49e8cd4bb4f9..35b20d0796db 100644
--- a/tools/testing/selftests/bpf/progs/verifier_ldsx.c
+++ b/tools/testing/selftests/bpf/progs/verifier_ldsx.c
@@ -450,6 +450,15 @@ void kfunc_root(void)
{
bpf_arena_alloc_pages(0, 0, 0, 0, 0);
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(void)
+{
+ return 0;
+}
#endif /* __BPF_FEATURE_ADDR_SPACE_CAST */
diff --git a/tools/testing/selftests/bpf/progs/verifier_zext.c b/tools/testing/selftests/bpf/progs/verifier_zext.c
index 572017fe28fb..dbfeeea4cf9f 100644
--- a/tools/testing/selftests/bpf/progs/verifier_zext.c
+++ b/tools/testing/selftests/bpf/progs/verifier_zext.c
@@ -381,6 +381,15 @@ __naked void zext_arena_cmpxchg32(void)
BPF_ATOMIC_OP(BPF_W, BPF_CMPXCHG, BPF_REG_1, BPF_REG_2, 0))
: __clobber_all);
}
+#else
+
+SEC("socket")
+__skip_without_arena
+__success
+int arena_unsupported(void)
+{
+ return 0;
+}
#endif
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v2 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (2 preceding siblings ...)
2026-10-28 20:41 ` [PATCH bpf-next v2 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 5/8] selftests/bpf: don't report arena tests as passing with an empty body Vineet Gupta
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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 and GCC says so:
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'
GCC is right; clang has the same warning but does not apply it to a
member access through a cast. With BPF_CFLAGS carrying -Werror this is
a build failure, today, for every prog that calls arena_base() and is
compiled by a GCC with arena support: verifier_arena.c,
verifier_arena_large.c, verifier_arena_globals1.c,
verifier_arena_globals2.c and arena_race.c.
Make the pointer opaque instead, which is what progs/stream.c already
does for its own open-coded copy of the same access. The bounds
analysis then has nothing to reason about, and every caller is covered
without a per-file opt-out that has to be maintained -- arena_race.c
was added without one and broke the bpf_gcc build.
The barrier is behind #ifdef __clang__ so clang keeps the plain macro.
It does not need it, and the barrier is not free: it blocks reuse of
the map address, so each call site reloads it. Across the five progs
and 20 call sites that is +35 instructions under GCC
(2528 -> 2563), about 1.4% on those objects and nothing elsewhere.
Clang output is unchanged, bit for bit.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
.../bpf/libarena/include/bpf_arena_common.h | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
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..4f2c6050215f 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,23 @@ 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;
+/*
+ * GCC's -Warray-bounds fires on the member access: the map definition the
+ * object file carries is smaller than struct bpf_arena. Make the pointer
+ * opaque to it, as the open-coded form in progs/stream.c already does.
+ * clang does not warn and does not need the barrier, which costs a reload
+ * of the map address at every call site.
+ */
+#ifdef __clang__
#define arena_base(map) ((void __arena *)((struct bpf_arena *)(map))->user_vm_start)
+#else
+#define arena_base(map) \
+({ \
+ struct bpf_arena *__ma = (void *)(map); \
+ barrier_var(__ma); \
+ (void __arena *)__ma->user_vm_start; \
+})
+#endif
#else /* when compiled as user space code */
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v2 5/8] selftests/bpf: don't report arena tests as passing with an empty body
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (3 preceding siblings ...)
2026-10-28 20:41 ` [PATCH bpf-next v2 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
` (2 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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
Some arena tests put the __BPF_FEATURE_ADDR_SPACE_CAST guard inside the
function rather than around it, so without arena support the program
still exists, its body is empty, and __success __retval(0) is met by
doing nothing. A reader sees
#584/1 verifier_arena/basic_alloc1_nosleep:OK
and concludes arena works. That is worse than the tests which vanish
entirely, since nothing hints anything is missing.
The guard was never needed to compile those bodies: without the
feature __arena is still btf_type_tag("arena"), which every compiler
accepts. What it avoided was the load failure that follows from no
addr_space_cast being emitted — but only for a body that dereferences
an arena pointer. The arena kfuncs do not check theirs at all: the
address parameter of bpf_arena_{alloc,free,reserve}_pages() carries the
__ign suffix, get_kfunc_arg_type() returns ARG_IGNORE for it, and
check_func_arg() returns before any register type check. So a test
that only passes pointers to those kfuncs needs no cast and runs fine.
test_loader checks skip_reason before it opens anything, so the body no
longer has to be empty to stay loadable: drop all 19 in-body guards.
They do not all need a tag in their place, which is why 19 guards go
and 7 tags arrive:
6 dereference, so they get __skip_without_arena and report SKIP:
basic_alloc1{,_nosleep}, basic_alloc2{,_nosleep}, big_alloc1,
access_reserved
11 never dereference, so they get no tag and run for real instead
of reporting a vacuous OK
2 big_alloc3 and big_alloc4, already inside the file's outer
whole-program guard, so the in-body one was redundant
The 7th tag goes on check_arena_arg_ret(), which has no guard at all
and is the one verifier_arena subtest that really fails under a stock
GCC.
Measured on bpf-next with a stock bpf-gcc, for verifier_arena and
verifier_arena_large:
verifier_arena/basic_alloc1 OK -> SKIP
verifier_arena/basic_alloc1_nosleep OK -> SKIP
verifier_arena/basic_alloc2 OK -> SKIP
verifier_arena/basic_alloc2_nosleep OK -> SKIP
verifier_arena_large/big_alloc1 OK -> SKIP
verifier_arena_large/access_reserved OK -> SKIP
verifier_arena/check_arena_arg_ret FAIL -> SKIP
the six that dereference stop claiming to pass, and check_arena_arg_ret
stops failing. The eleven that do not dereference stay OK, having gone
from an empty body to actually running.
Removing the guards exposes the arena_base() calls they were hiding.
The previous patch made that macro opaque to GCC's -Warray-bounds, so
nothing is needed here beyond the guards going away.
arena_kfunc.c, verifier_arena_globals1.c and verifier_arena_globals2.c
have the same shape and are included here: nine more guards go, seven
more tags arrive, and seven more subtests go OK -> SKIP. arena_kfunc.c
had six of those, the most of the three.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
.../testing/selftests/bpf/progs/arena_kfunc.c | 18 +++--------
.../selftests/bpf/progs/verifier_arena.c | 31 +++----------------
.../bpf/progs/verifier_arena_globals1.c | 6 ++--
.../bpf/progs/verifier_arena_globals2.c | 3 +-
.../bpf/progs/verifier_arena_large.c | 16 ++--------
5 files changed, 14 insertions(+), 60 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/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index 3b3410802825..21b66d51c61e 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,7 +482,6 @@ 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;
}
@@ -765,6 +743,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 34f0a0f88e12..6ef99793cac1 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,7 +172,6 @@ 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;
}
@@ -292,7 +286,6 @@ SEC("socket")
__success __retval(0)
int big_alloc3(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *pages;
u64 i;
int err = 0;
@@ -314,8 +307,6 @@ int big_alloc3(void *ctx)
bpf_arena_free_pages(&arena, pages, LARGE_PAGE_CNT);
return err;
-#endif
- return 0;
}
/* SYSCALL programs are always sleepable. */
@@ -323,7 +314,6 @@ SEC("syscall")
__success __retval(0)
int big_alloc4(void *ctx)
{
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
char __arena *pages;
u64 i;
int err = 0;
@@ -345,8 +335,6 @@ int big_alloc4(void *ctx)
bpf_arena_free_pages(&arena, pages, LARGE_PAGE_CNT);
return err;
-#endif
- return 0;
}
#else
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread* [PATCH bpf-next v2 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (4 preceding siblings ...)
2026-10-28 20:41 ` [PATCH bpf-next v2 5/8] selftests/bpf: don't report arena tests as passing with an empty body Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
2026-10-28 20:41 ` [PATCH bpf-next v2 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support Vineet Gupta
7 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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
Four stream tests fail with BPF_GCC:
test_stream_arena_fault_address:FAIL:stream__open_and_load unexpected error: -13
#479 stream_oversize:FAIL
#480 stream_partial_read:FAIL
#482 stream_syscall:FAIL
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.
Fifteen results go FAIL -> OK -- every test that loads this object, not
just the arena ones:
stream_arena_fault_address stream_oversize
stream_fd stream_fd/blocking stream_partial_read
stream_fd/edge stream_fd/empty stream_success
stream_fd/hup stream_fd/nmi_epoll stream_success/stream_arena_callback_fault
stream_fd/open stream_fd/nonblock stream_success/stream_arena_subprog_fault
stream_syscall
and the five stream_arena_fault_address subtests become visible for the
first time (read, write, xchg, cmpxchg and load_acquire faults).
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
tools/testing/selftests/bpf/progs/stream.c | 7 ++++---
1 file changed, 4 insertions(+), 3 deletions(-)
diff --git a/tools/testing/selftests/bpf/progs/stream.c b/tools/testing/selftests/bpf/progs/stream.c
index 1ae015c4e6b4..dc56779cac06 100644
--- a/tools/testing/selftests/bpf/progs/stream.c
+++ b/tools/testing/selftests/bpf/progs/stream.c
@@ -5,7 +5,6 @@
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
#include "bpf_experimental.h"
-#include <bpf_arena_common.h>
struct arr_elem {
struct bpf_res_spin_lock lock;
@@ -381,9 +380,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;
}
@@ -404,9 +404,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* [PATCH bpf-next v2 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (5 preceding siblings ...)
2026-10-28 20:41 ` [PATCH bpf-next v2 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
2026-10-28 21:22 ` bot+bpf-ci
2026-10-28 20:41 ` [PATCH bpf-next v2 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support Vineet Gupta
7 siblings, 1 reply; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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
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 [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v2 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
2026-10-28 20:41 ` [PATCH bpf-next v2 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
@ 2026-10-28 21:22 ` bot+bpf-ci
2026-10-28 23:14 ` Vineet Gupta
0 siblings, 1 reply; 13+ messages in thread
From: bot+bpf-ci @ 2026-10-28 21:22 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: 2998 bytes --]
> selftests/bpf: write the mixed arena/scalar ALU tests in asm
TL;DR: Test comment only: the new verifier_arena.c comment claiming the r9
arena load is needed for addr_space_cast looks stale, since libbpf now binds
the arena via fd_array. Suggest dropping or rewording it.
> 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")
Is this comment still accurate? Commit cd7a395838cb ("libbpf: Bind the arena
of an object to every program in it") is already in the base of this series.
With it, bpf_object_load_prog() calls bpf_object__bind_arena(), which puts
the object's arena into a continuous fd_array when the kernel supports
fd_array_cnt:
obj->fd_array[0] = map->fd;
load_attr->fd_array = obj->fd_array;
load_attr->fd_array_cnt = obj->fd_array_used;
The kernel binds that array before it looks at any insn:
process_fd_array_continuous()
add_fd_from_fd_array()
__add_used_map()
check_map_prog_compatibility()
env->prog->aux->arena = (void *)map;
This happens for every program in verifier_arena.o, including these two,
whether or not an insn refers to the map. The addr_space_cast check in
check_alu_op() (if (!env->prog->aux->arena)) and the prevent_zext/needs_zext
logic in adjust_reg_min_max_vals() only look at prog->aux->arena, so
removing the r9 load would not change what either test does with a kernel and
libbpf from this tree.
The comment also describes the mechanism differently from what happens.
libbpf only relocates the ld_imm64 to the map fd, and the verifier records
the map as used in add_used_map() during resolve_pseudo_ldimm64().
The log of the libbpf commit describes referring to the arena artificially,
as in asm volatile("" :: "r"(&arena)), as something that change makes
unnecessary. The r9 load would only matter on a kernel that rejects
fd_array_cnt, or with the light skeleton loader, which test_loader does not
use.
The same four comment lines are repeated word for word above
mixed_arena_scalar_alu64_arena_first(). Could the comment be dropped
together with the r9 load, or be reworded to say the load is a fallback for
kernels where libbpf cannot bind the arena through fd_array_cnt?
---
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/40768193895
^ permalink raw reply [flat|nested] 13+ messages in thread* Re: [PATCH bpf-next v2 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
2026-10-28 21:22 ` bot+bpf-ci
@ 2026-10-28 23:14 ` Vineet Gupta
0 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 23:14 UTC (permalink / raw)
To: bot+bpf-ci, 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,
martin.lau, mason
On 10/28/26 2:22 PM, bot+bpf-ci@kernel.org wrote:
>> selftests/bpf: write the mixed arena/scalar ALU tests in asm
> TL;DR: Test comment only: the new verifier_arena.c comment claiming the r9
> arena load is needed for addr_space_cast looks stale, since libbpf now binds
> the arena via fd_array. Suggest dropping or rewording it.
>
>> 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")
> Is this comment still accurate? Commit cd7a395838cb ("libbpf: Bind the arena
> of an object to every program in it") is already in the base of this series.
> With it, bpf_object_load_prog() calls bpf_object__bind_arena(), which puts
> the object's arena into a continuous fd_array when the kernel supports
> fd_array_cnt:
>
> obj->fd_array[0] = map->fd;
> load_attr->fd_array = obj->fd_array;
> load_attr->fd_array_cnt = obj->fd_array_used;
>
> The kernel binds that array before it looks at any insn:
>
> process_fd_array_continuous()
> add_fd_from_fd_array()
> __add_used_map()
> check_map_prog_compatibility()
> env->prog->aux->arena = (void *)map;
>
> This happens for every program in verifier_arena.o, including these two,
> whether or not an insn refers to the map. The addr_space_cast check in
> check_alu_op() (if (!env->prog->aux->arena)) and the prevent_zext/needs_zext
> logic in adjust_reg_min_max_vals() only look at prog->aux->arena, so
> removing the r9 load would not change what either test does with a kernel and
> libbpf from this tree.
>
> The comment also describes the mechanism differently from what happens.
> libbpf only relocates the ld_imm64 to the map fd, and the verifier records
> the map as used in add_used_map() during resolve_pseudo_ldimm64().
>
> The log of the libbpf commit describes referring to the arena artificially,
> as in asm volatile("" :: "r"(&arena)), as something that change makes
> unnecessary. The r9 load would only matter on a kernel that rejects
> fd_array_cnt, or with the light skeleton loader, which test_loader does not
> use.
>
> The same four comment lines are repeated word for word above
> mixed_arena_scalar_alu64_arena_first().
Will remove the duplication.
> Could the comment be dropped
> together with the r9 load, or be reworded to say the load is a fallback for
> kernels where libbpf cannot bind the arena through fd_array_cnt?
Yeah the comment mentioned r9 is totally weird, will simplify it.
Thx,
-Vineet
> ---
> 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/40768193895
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH bpf-next v2 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support
2026-10-28 20:41 [PATCH bpf-next v2 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (6 preceding siblings ...)
2026-10-28 20:41 ` [PATCH bpf-next v2 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
@ 2026-10-28 20:41 ` Vineet Gupta
7 siblings, 0 replies; 13+ messages in thread
From: Vineet Gupta @ 2026-10-28 20:41 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. Two tests report OK
under a stock bpf-gcc on that basis: arena_arg and arena_autobind. The
other two, arena_member and arena_member_render, need a testmod BTF
carrying the arena type tag and are not reached without one, but are
fixed the same way.
These are not driven by test_loader, so __skip_without_arena does not
reach them. Use the flag-in-.data pattern linked_externs.c already
uses for the same situation: the program exports skip_tests, and the
host checks it before attaching and reports SKIP.
Signed-off-by: Vineet Gupta <vineet.gupta@linux.dev>
---
.../bpf/prog_tests/test_struct_ops_arena.c | 27 +++++++++++++++++++
.../selftests/bpf/progs/struct_ops_arena.c | 10 +++++++
.../bpf/progs/struct_ops_arena_autobind.c | 10 +++++++
.../bpf/progs/struct_ops_arena_member.c | 10 +++++++
.../progs/struct_ops_arena_member_render.c | 10 +++++++
5 files changed, 67 insertions(+)
diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c
index e7580b9f0fc2..11b7dab77ff7 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_arena.c
@@ -31,6 +31,12 @@ static void arena_arg(void)
if (!ASSERT_OK_PTR(skel, "struct_ops_arena__open_and_load"))
return;
+ if (skel->data->skip_tests) {
+ printf("%s:SKIP: no addr_space_cast support in the BPF compiler\n", __func__);
+ test__skip();
+ goto out;
+ }
+
link = bpf_map__attach_struct_ops(skel->maps.testmod_arena);
if (!ASSERT_OK_PTR(link, "attach_struct_ops"))
goto out;
@@ -142,6 +148,13 @@ static void arena_member(void)
if (!ASSERT_OK_PTR(skel, "struct_ops_arena_member__open_and_load"))
return;
+ if (skel->data->skip_tests) {
+ printf("%s:SKIP: no addr_space_cast support in the BPF compiler\n", __func__);
+ test__skip();
+ struct_ops_arena_member__destroy(skel);
+ return;
+ }
+
attach_and_trigger(skel->maps.testmod_arena_member, skel->progs.trigger);
struct_ops_arena_member__destroy(skel);
}
@@ -186,6 +199,13 @@ static void arena_autobind(void)
assert_prog_map(bpf_program__fd(skel->progs.test_arena_cb), info.id, "test_arena_cb");
assert_prog_map(bpf_program__fd(skel->progs.unrelated), info.id, "unrelated");
+ if (skel->data->skip_tests) {
+ printf("%s:SKIP: no addr_space_cast support in the BPF compiler\n", __func__);
+ test__skip();
+ struct_ops_arena_autobind__destroy(skel);
+ return;
+ }
+
attach_and_trigger(skel->maps.testmod_arena_autobind, skel->progs.trigger);
out:
struct_ops_arena_autobind__destroy(skel);
@@ -268,6 +288,13 @@ static void arena_member_render(void)
if (!ASSERT_OK_PTR(skel, "struct_ops_arena_member_render__open_and_load"))
return;
+ if (skel->data->skip_tests) {
+ printf("%s:SKIP: no addr_space_cast support in the BPF compiler\n", __func__);
+ test__skip();
+ struct_ops_arena_member_render__destroy(skel);
+ return;
+ }
+
attach_and_trigger(skel->maps.testmod_arena_member_render, skel->progs.trigger);
struct_ops_arena_member_render__destroy(skel);
}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena.c b/tools/testing/selftests/bpf/progs/struct_ops_arena.c
index 8aa8639df91f..612ca868c8fe 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena.c
@@ -83,6 +83,16 @@ struct bpf_testmod_ops3 testmod_arena = {
.test_arena_multislot = (void *)test_arena_multislot_cb,
};
+/*
+ * These tests are not driven by test_loader, so __skip_without_arena
+ * cannot reach them; the host side reads this flag and skips instead.
+ */
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+bool skip_tests __attribute((__section__(".data"))) = false;
+#else
+bool skip_tests = true;
+#endif
+
SEC("syscall")
int trigger(void *ctx)
{
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..de026d901dcb 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_autobind.c
@@ -42,6 +42,16 @@ int unrelated(void *ctx)
return 0;
}
+/*
+ * These tests are not driven by test_loader, so __skip_without_arena
+ * cannot reach them; the host side reads this flag and skips instead.
+ */
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+bool skip_tests __attribute((__section__(".data"))) = false;
+#else
+bool skip_tests = true;
+#endif
+
SEC("syscall")
int trigger(void *ctx)
{
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..4ec0aead3e95 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_arena_member.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_arena_member.c
@@ -77,6 +77,16 @@ struct bpf_testmod_ops3 testmod_arena_member = {
.test_arena_member = (void *)test_arena_member_cb,
};
+/*
+ * These tests are not driven by test_loader, so __skip_without_arena
+ * cannot reach them; the host side reads this flag and skips instead.
+ */
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+bool skip_tests __attribute((__section__(".data"))) = false;
+#else
+bool skip_tests = true;
+#endif
+
SEC("syscall")
int trigger(void *ctx)
{
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..3ccbc6e71626 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
@@ -60,6 +60,16 @@ struct bpf_testmod_ops3 testmod_arena_member_render = {
.test_arena_member = (void *)test_arena_member_render_cb,
};
+/*
+ * These tests are not driven by test_loader, so __skip_without_arena
+ * cannot reach them; the host side reads this flag and skips instead.
+ */
+#ifdef __BPF_FEATURE_ADDR_SPACE_CAST
+bool skip_tests __attribute((__section__(".data"))) = false;
+#else
+bool skip_tests = true;
+#endif
+
SEC("syscall")
int trigger(void *ctx)
{
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 13+ messages in thread