* [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs
@ 2026-11-02 22:07 Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
` (7 more replies)
0 siblings, 8 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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
This series came out of testing a still in-works __arena support for
BPF_GCC and anomalies seen in the A/B reports.
BPF_GCC selftest builds reports a number of things that are not true.
- Tests the compiler cannot build do not appear at all, so the
report is silent about them
- Others are compiled down to an empty body and report OK despite
not even been executed
- Two programs that fail to verify take four unrelated tests down with
them, because everything in that object is loaded together.
- Unconditional gating on __clang__ when newer GCC support for those
is coming online.
Only 7/8 needs the in-works arena support to be exercised at all; the
rest are measured below with a stock bpf-gcc. Nothing in the series
changes what clang emits.
What each patch does, and what it measurably changes. Numbers are
test_progs on bpf-next with a stock bpf-gcc:
1/8: Gate the callx tests on CAN_USE_CALLX, which also accepts a GCC
defining __BPF_FEATURE_CALLX.
-> 79 subtests newly visible, 78 OK and one @unpriv SKIP; the two
dummies retire; callx_rodata_lskel SKIP -> OK.
2/8: Keep clang-only warning flags out of GCC builds.
3/8: Add __skip_without_arena, and give each arena guard an #else
dummy carrying it.
-> 7 subtests newly visible, all SKIP; arena_kfunc_jit OK -> SKIP.
4/8: Make arena_base() opaque to GCC's -Warray-bounds, behind
#ifdef __clang__ since clang does not warn.
-> no verdict change. +35 insns over 20 call sites under GCC;
clang output unchanged, bit for bit.
5/8: Tag the tests whose body dereferences an arena pointer, and only
those; drop the in-body guards.
-> 13 subtests OK -> SKIP, check_arena_arg_ret FAIL -> SKIP, and
verifier_arena itself FAIL -> OK. The rest now actually run.
6/8: Cast explicitly with bpf_addr_space_cast() in the arena subprog
and callback faults, as the sibling tests already do.
-> 15 FAIL -> OK, and 5 subtests newly visible.
7/8: Write the mixed arena/scalar ALU tests in asm, rather than a
volatile register variable GCC may optimise away.
-> not exercised by a stock bpf-gcc.
8/8: trigger() in the four struct_ops arena progs is guarded in-body
and test_loader does not drive them, so the tag cannot reach
them; use the flag-in-.data pattern from linked_externs.c.
-> 2 tests stop reporting a vacuous OK. The other two need a
testmod BTF carrying the arena tag, so are unreached here.
Totals for the series. Nothing regresses:
FAIL 472 -> 456 OK -> FAIL regressions: 0
OK 7061 -> 7141
SKIP 753 -> 779
Two things worth re-iterating explicitly:
- 1/8 needs a GCC defining __BPF_FEATURE_CALLX, in bleeding-edge
but not released yet.
- 7/8, needs a GCC with arena support; that work is coming along
but not yet ready for public review.
v1 -> v2:
- 1/8: the __skip() reason named only x86/arm64 while CAN_USE_CALLX
also accepts riscv64. Say "arch JIT" instead of listing them, so
the string cannot go stale the next time an arch is added. (bot)
- 3/8: jit_insns_arena.c was missed. Both of its programs are inside
the guard, so without arena support the object has none at all and
RUN_TESTS(jit_insns_arena) ran zero subtests without saying so.
(bot)
- 5/8: commit message only. It cited arena_types to argue the kfunc
tests need no cast, but the address parameters carry the __ign
suffix, so get_kfunc_arg_type() returns ARG_IGNORE and
check_func_arg() returns before any register type check.
Conclusion unchanged, for a better reason. (bot)
- 5/8: arena_kfunc.c, verifier_arena_globals1.c and
verifier_arena_globals2.c had the same empty-body shape and were
missed; nine more guards go and seven more tags arrive.
arena_kfunc.c alone had eight subtests reporting a vacuous OK.
- 8/8: new patch, for the four struct_ops arena progs.
The three bot findings prompted a sweep of every prog with an arena
guard, which is where the last two came from. The totals above were
re-measured on the series as posted.
v2 -> v3:
- Rebased on bpf-next.
- 1/8: the bot asked about data_in_arena.c, whose use_ops() gated an
indirect call on __clang__ with no arch condition. That file has
since gone in 3c13b2e5a40e ("selftests/bpf: Remove tests for global
data in arena"), so nothing is left to convert. The missing word in
the commit message is fixed. (bot)
- 7/8: the comment on the r9 load said it was what makes libbpf record
the arena, which stopped being true with cd7a395838cb ("libbpf: Bind
the arena of an object to every program in it"). The load is now
only a fallback for a kernel without fd_array_cnt and for the light
skeleton; say that instead. (bot)
Link: https://lore.kernel.org/bpf/20261028000411.4010967-1-vineet.gupta@linux.dev/ [v1]
Link: https://lore.kernel.org/bpf/20261028204158.1021284-1-vineet.gupta@linux.dev/ [v2]
Vineet Gupta (8):
selftests/bpf: Build callx tests with GCC that defines
__BPF_FEATURE_CALLX
selftests/bpf: don't pass clang-only warning flags to GCC
selftests/bpf: report SKIP for arena tests a compiler cannot build
selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
selftests/bpf: don't report arena tests as passing with an empty body
selftests/bpf: cast explicitly in the arena subprog and callback
faults
selftests/bpf: write the mixed arena/scalar ALU tests in asm
selftests/bpf: skip the struct_ops arena tests without compiler
support
tools/testing/selftests/bpf/Makefile | 8 +-
.../testing/selftests/bpf/Makefile.buildvars | 6 +-
.../bpf/libarena/include/bpf_arena_common.h | 16 +++
.../bpf/prog_tests/callx_rodata_lskel.c | 2 +-
.../bpf/prog_tests/test_struct_ops_arena.c | 27 ++++
.../testing/selftests/bpf/progs/arena_kfunc.c | 18 +--
.../selftests/bpf/progs/arena_kfunc_jit.c | 9 ++
tools/testing/selftests/bpf/progs/bpf_misc.h | 21 +++
.../selftests/bpf/progs/callx_rodata.c | 4 +-
.../bpf/progs/compute_live_registers.c | 10 ++
.../selftests/bpf/progs/jit_insns_arena.c | 10 ++
tools/testing/selftests/bpf/progs/stream.c | 7 +-
.../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 ++
.../selftests/bpf/progs/verifier_arena.c | 131 +++++++++---------
.../bpf/progs/verifier_arena_globals1.c | 6 +-
.../bpf/progs/verifier_arena_globals2.c | 3 +-
.../bpf/progs/verifier_arena_large.c | 24 ++--
.../selftests/bpf/progs/verifier_callx.c | 5 +-
.../bpf/progs/verifier_callx_rodata.c | 5 +-
.../selftests/bpf/progs/verifier_ldsx.c | 9 ++
.../selftests/bpf/progs/verifier_zext.c | 9 ++
24 files changed, 253 insertions(+), 117 deletions(-)
--
2.53.0-Meta
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH bpf-next v3 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
` (6 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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 run that
previously did not exist in the report, 78 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 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] 15+ messages in thread
* [PATCH bpf-next v3 2/8] selftests/bpf: don't pass clang-only warning flags to GCC
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build Vineet Gupta
` (5 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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 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] 15+ messages in thread
* [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:36 ` Alexei Starovoitov
2026-11-02 22:07 ` [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
` (4 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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 b17a6dfc100d..121f8cea8c25 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -728,6 +728,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] 15+ messages in thread
* [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (2 preceding siblings ...)
2026-11-02 22:07 ` [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:38 ` Alexei Starovoitov
2026-11-02 22:07 ` [PATCH bpf-next v3 5/8] selftests/bpf: don't report arena tests as passing with an empty body Vineet Gupta
` (3 subsequent siblings)
7 siblings, 1 reply; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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] 15+ messages in thread
* [PATCH bpf-next v3 5/8] selftests/bpf: don't report arena tests as passing with an empty body
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (3 preceding siblings ...)
2026-11-02 22:07 ` [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
` (2 subsequent siblings)
7 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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 121f8cea8c25..27ffd74567ec 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;
}
@@ -780,6 +758,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] 15+ messages in thread
* [PATCH bpf-next v3 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (4 preceding siblings ...)
2026-11-02 22:07 ` [PATCH bpf-next v3 5/8] selftests/bpf: don't report arena tests as passing with an empty body Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support Vineet Gupta
7 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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] 15+ messages in thread
* [PATCH bpf-next v3 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (5 preceding siblings ...)
2026-11-02 22:07 ` [PATCH bpf-next v3 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support Vineet Gupta
7 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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 | 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 27ffd74567ec..3b13f6aa6b62 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -539,53 +539,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], 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)
+{
+ /*
+ * 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], 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] 15+ messages in thread
* [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
` (6 preceding siblings ...)
2026-11-02 22:07 ` [PATCH bpf-next v3 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
@ 2026-11-02 22:07 ` Vineet Gupta
2026-11-02 22:39 ` Alexei Starovoitov
7 siblings, 1 reply; 15+ messages in thread
From: Vineet Gupta @ 2026-11-02 22:07 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] 15+ messages in thread
* Re: [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build
2026-11-02 22:07 ` [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build Vineet Gupta
@ 2026-11-02 22:36 ` Alexei Starovoitov
2026-11-03 0:24 ` Vineet Gupta
0 siblings, 1 reply; 15+ messages in thread
From: Alexei Starovoitov @ 2026-11-02 22:36 UTC (permalink / raw)
To: Vineet Gupta, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, john.fastabend, shuah, linux-kernel,
linux-kselftest, jose.marchesi, david.faust
On Mon, Nov 02, 2026 at 02:07 PM Vineet Gupta <vineet.gupta@linux.dev> wrote:
> 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.
How about the following approach instead...
looks much simpler to me...
pw-bot: cr
diff --git a/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c b/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
index b5a01cb..16c3dd9 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 d7f706c..4ec840b 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/jit_insns_arena.c b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
index 95736ea..77d135a 100644
--- a/tools/testing/selftests/bpf/progs/jit_insns_arena.c
+++ b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
@@ -106,6 +106,14 @@ 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_large.c b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
index dbdea14..428b984 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena_large.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
@@ -181,7 +181,6 @@ int free_reserved(void *ctx)
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 +219,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 +290,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 +314,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 +343,6 @@ 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";
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
2026-11-02 22:07 ` [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
@ 2026-11-02 22:38 ` Alexei Starovoitov
2026-11-03 0:15 ` Vineet Gupta
0 siblings, 1 reply; 15+ messages in thread
From: Alexei Starovoitov @ 2026-11-02 22:38 UTC (permalink / raw)
To: Vineet Gupta, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, john.fastabend, shuah, linux-kernel,
linux-kselftest, jose.marchesi, david.faust
On Mon, Nov 02, 2026 at 02:07 PM Vineet Gupta <vineet.gupta@linux.dev> wrote:
> 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.
stream.c already has barrier_var() for both compilers.
How about this one instead:
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 82aafe8..0eba529 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 1ae015c..b4d88d0 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);
/*
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support
2026-11-02 22:07 ` [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support Vineet Gupta
@ 2026-11-02 22:39 ` Alexei Starovoitov
2026-11-03 0:17 ` Vineet Gupta
0 siblings, 1 reply; 15+ messages in thread
From: Alexei Starovoitov @ 2026-11-02 22:39 UTC (permalink / raw)
To: Vineet Gupta, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, john.fastabend, shuah, linux-kernel,
linux-kselftest, jose.marchesi, david.faust
On Mon, Nov 02, 2026 at 02:07 PM Vineet Gupta <vineet.gupta@linux.dev> wrote:
> 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.
cast_kern() is cleaner...
It's a nop when the compiler supports arena.
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena.c b/tools/testing/selftests/bpf/progs/struct_ops_arena.c
index 8aa8639..3fffa24 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 495faa0..3455ca9 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 a2218cd..afbe59f 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 09b4c74..c4e134e 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;
}
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds
2026-11-02 22:38 ` Alexei Starovoitov
@ 2026-11-03 0:15 ` Vineet Gupta
0 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-03 0:15 UTC (permalink / raw)
To: Alexei Starovoitov, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, john.fastabend, shuah, linux-kernel,
linux-kselftest, jose.marchesi, david.faust
On 11/2/26 2:38 PM, Alexei Starovoitov wrote:
> On Mon, Nov 02, 2026 at 02:07 PM Vineet Gupta <vineet.gupta@linux.dev> wrote:
>> 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.
> stream.c already has barrier_var() for both compilers.
>
> How about this one instead:
Yeah definitely much better. Just to be clear the intent of series is to
not make current compiler look better o anything. As I mentioned in the
cover, this was during in my in-works arena compiler testing, ....
Thx for the quick feedback.
-Vineet
> 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 82aafe8..0eba529 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 1ae015c..b4d88d0 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);
> /*
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support
2026-11-02 22:39 ` Alexei Starovoitov
@ 2026-11-03 0:17 ` Vineet Gupta
0 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-03 0:17 UTC (permalink / raw)
To: Alexei Starovoitov, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, john.fastabend, shuah, linux-kernel,
linux-kselftest, jose.marchesi, david.faust
On 11/2/26 2:39 PM, Alexei Starovoitov wrote:
> On Mon, Nov 02, 2026 at 02:07 PM Vineet Gupta <vineet.gupta@linux.dev> wrote:
>> 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.
> cast_kern() is cleaner...
> It's a nop when the compiler supports arena.
Nice, definitely better than what I had since now more code is exercised
than being skipped.
Thx,
-Vineet
> diff --git a/tools/testing/selftests/bpf/progs/struct_ops_arena.c b/tools/testing/selftests/bpf/progs/struct_ops_arena.c
> index 8aa8639..3fffa24 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 495faa0..3455ca9 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 a2218cd..afbe59f 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 09b4c74..c4e134e 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;
> }
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build
2026-11-02 22:36 ` Alexei Starovoitov
@ 2026-11-03 0:24 ` Vineet Gupta
0 siblings, 0 replies; 15+ messages in thread
From: Vineet Gupta @ 2026-11-03 0:24 UTC (permalink / raw)
To: Alexei Starovoitov, bpf
Cc: daniel, andrii, eddyz87, memxor, martin.lau, song, yonghong.song,
jolsa, emil, ihor.solodrai, john.fastabend, shuah, linux-kernel,
linux-kselftest, jose.marchesi, david.faust
On 11/2/26 2:36 PM, Alexei Starovoitov wrote:
> On Mon, Nov 02, 2026 at 02:07 PM Vineet Gupta <vineet.gupta@linux.dev> wrote:
>> 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.
> How about the following approach instead...
> looks much simpler to me...
Agree: it does look simpler and they uncover more code to be tested now
than being skipped.
Thx,
-Vineet
> pw-bot: cr
>
> diff --git a/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c b/tools/testing/selftests/bpf/progs/arena_kfunc_jit.c
> index b5a01cb..16c3dd9 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 d7f706c..4ec840b 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/jit_insns_arena.c b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
> index 95736ea..77d135a 100644
> --- a/tools/testing/selftests/bpf/progs/jit_insns_arena.c
> +++ b/tools/testing/selftests/bpf/progs/jit_insns_arena.c
> @@ -106,6 +106,14 @@ 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_large.c b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
> index dbdea14..428b984 100644
> --- a/tools/testing/selftests/bpf/progs/verifier_arena_large.c
> +++ b/tools/testing/selftests/bpf/progs/verifier_arena_large.c
> @@ -181,7 +181,6 @@ int free_reserved(void *ctx)
> 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 +219,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 +290,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 +314,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 +343,6 @@ 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";
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2026-11-03 0:25 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-02 22:07 [PATCH bpf-next v3 0/8] selftests/bpf: cleanup bpf-gcc runs Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 1/8] selftests/bpf: Build callx tests with GCC that defines __BPF_FEATURE_CALLX Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 2/8] selftests/bpf: don't pass clang-only warning flags to GCC Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 3/8] selftests/bpf: report SKIP for arena tests a compiler cannot build Vineet Gupta
2026-11-02 22:36 ` Alexei Starovoitov
2026-11-03 0:24 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 4/8] selftests/bpf: make arena_base() opaque to GCC's -Warray-bounds Vineet Gupta
2026-11-02 22:38 ` Alexei Starovoitov
2026-11-03 0:15 ` Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 5/8] selftests/bpf: don't report arena tests as passing with an empty body Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 6/8] selftests/bpf: cast explicitly in the arena subprog and callback faults Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 7/8] selftests/bpf: write the mixed arena/scalar ALU tests in asm Vineet Gupta
2026-11-02 22:07 ` [PATCH bpf-next v3 8/8] selftests/bpf: skip the struct_ops arena tests without compiler support Vineet Gupta
2026-11-02 22:39 ` Alexei Starovoitov
2026-11-03 0:17 ` Vineet Gupta
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®