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