summaryrefslogtreecommitdiff
path: root/tools/testing
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 08:10:23 -1000
committerLinus Torvalds <torvalds@linux-foundation.org>2026-10-29 08:10:23 -1000
commit46806af187f448b0d5481f284e7c900a57ee6a6d (patch)
tree4a664c1e3f24c91ad6df8b5c5e3bf2fd10b4ee1d /tools/testing
parentd011f648a708b231f7ef28c18ff30c815b07fe56 (diff)
parentc8a8a68d3ab4a06c8f5a13a8c00275351c8239b4 (diff)
Merge tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf
Pull bpf fixes from Alexei Starovoitov: - Remove BPF_F_ARENA_SCALAR flag and .arena.data section in libbpf that were added for rust-bpf in this merge window. We found a cleaner approach that does it on compiler side instead of kernel/libbpf. No released kernel has them (Alexei Starovoitov) - Fix bpf_send_signal_task() returning -EPERM when it is called from a kernel thread (Aditya Sharma) - Various fixes (Alexei Starovoitov): - Use emit_rsb_call() to call bpf_unwind_target() in x86 JIT to fix objtool warning during the build - Fix scalar evolution: clear delta of a widened register, don't widen a pointer to a buffer, don't clamp a register whose type changed in the loop, and reset the range of a clamped packet pointer - Finish the insn in the state that maybe_fork_scalars() splits off, so that pruning doesn't drop it at the head of a counted loop - Preserve the registers that carry stack arguments across arch_bpf_timed_may_goto() on x86-64, arm64 and riscv64 - Don't re-sort subprog_info[] for every new subprogram - Fix flaky map_kptr and rcu_reentry selftests - Use memmove() in bpf_skb_load_bytes(), bpf_skb_load_bytes_relative() and bpf_xdp_load_bytes(), since the destination can be a slice of the same packet (Ayushman Rout) - Fixes and follow-ups for scalar evolution: reject pointer operands in 32-bit recurrences and latches, don't intersect with the current register range on clamp to avoid a false 'verifier bug', avoid false infinite loop reports, and recognize a 32-bit copy of the induction variable (Eduard Zingerman) - Fix out-of-bounds write to btf->types[] for split BTF and propagate btf_add_type() errors in btf_check_all_metas() (Feng Yang) - Fix use-after-free in bpf_timer callback that fires after its inner map was freed (Mani Tofigh) - Fix x86 JIT stopping before jumps converge: size backward jumps by the insns they jump over (Mykyta Yatsenko) - Fix use-after-free of struct_ops trampoline: dissociate struct_ops progs before the grace period of the map (Qiliang Yuan) - Validate stack argument reads against the caller state to fix out-of-bounds read in the verifier, and reject callbacks with stack arguments (Weiming Shi) - Tie borrowed iterator results and pointers loaded from trusted fields to the lifetime of their source, so that iterator next, destroy and reference release invalidate them (Xu Yunxiang) * tag 'bpf-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf: (48 commits) bpf: Don't re-sort subprog_info[] for every new subprogram bpf: Fix out-of-bounds btf->types write for split BTF selftests/bpf: Test stack arguments across may_goto bpf: Keep stack arguments in arch_bpf_timed_may_goto() selftests/bpf: Test maybe_fork_scalars() at the head of a counted loop bpf: Finish the insn in the state that maybe_fork_scalars() splits off selftests/bpf: Test the clamp of pointers in scalar evolution bpf: Reset the range of a packet pointer that scalar evolution clamps bpf: Don't clamp a register whose type changed in the loop selftests/bpf: Test a loop that moves a pointer to a buffer bpf: Don't widen a pointer to a buffer in scalar evolution selftests/bpf: Avoid a page fault in rcu_reentry while the handler is armed selftests/bpf: Wait for the last maps of map_kptr to be freed selftests/bpf: Test that a widened register is not linked bpf: Clear delta of a register that scalar evolution widens bpf, x86: Remove r0_on_stack from fetching atomics in arena bpf: Remove BPF_F_ARENA_SCALAR selftests/bpf: Remove tests for arena access through numbers libbpf: Keep format strings of bpf_printk() in .rodata libbpf: Remove .arena.data ...
Diffstat (limited to 'tools/testing')
-rw-r--r--tools/testing/selftests/bpf/Makefile12
-rw-r--r--tools/testing/selftests/bpf/Makefile.buildvars25
-rw-r--r--tools/testing/selftests/bpf/Makefile.skel18
-rw-r--r--tools/testing/selftests/bpf/prog_tests/arena_scalar_blinded.c21
-rw-r--r--tools/testing/selftests/bpf/prog_tests/data_in_arena.c216
-rw-r--r--tools/testing/selftests/bpf/prog_tests/jit_insns.c59
-rw-r--r--tools/testing/selftests/bpf/prog_tests/map_kptr.c5
-rw-r--r--tools/testing/selftests/bpf/prog_tests/rcu_reentry.c19
-rw-r--r--tools/testing/selftests/bpf/prog_tests/send_signal.c106
-rw-r--r--tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c37
-rw-r--r--tools/testing/selftests/bpf/prog_tests/verifier.c2
-rw-r--r--tools/testing/selftests/bpf/progs/arena_noarena.c15
-rw-r--r--tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c10
-rw-r--r--tools/testing/selftests/bpf/progs/data_in_arena.c112
-rw-r--r--tools/testing/selftests/bpf/progs/data_in_arena_decl.c37
-rw-r--r--tools/testing/selftests/bpf/progs/data_in_arena_extern.c20
-rw-r--r--tools/testing/selftests/bpf/progs/data_in_arena_fail.c20
-rw-r--r--tools/testing/selftests/bpf/progs/data_in_arena_nomap.c20
-rw-r--r--tools/testing/selftests/bpf/progs/data_in_arena_rust.rs73
-rw-r--r--tools/testing/selftests/bpf/progs/iters_testmod.c370
-rw-r--r--tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c16
-rw-r--r--tools/testing/selftests/bpf/progs/test_send_signal_kern.c29
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_arena.c15
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_arena_scalar.c1155
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_bounds.c41
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_scev.c504
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_stack_arg.c119
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c121
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_stack_ptr.c30
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_vfs_accept.c17
-rw-r--r--tools/testing/selftests/bpf/progs/verifier_vfs_reject.c19
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_testmod.c25
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_testmod.h4
-rw-r--r--tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h5
-rw-r--r--tools/testing/selftests/bpf/test_loader.c2
35 files changed, 1520 insertions, 1779 deletions
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 1e4af62e8312..c99e0f623e10 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -423,16 +423,6 @@ $(LIBARENA_ASAN_SKEL): $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(LIBARENA_BPF_DEPS)
+$(MAKE) -C libarena libarena_asan.skel.h $(LIBARENA_MAKE_ARGS)
endif
-# #![no_std] looks for compiler_builtins too. Nothing of it is used.
-ifneq ($(RUST_CORE),)
-$(RUST_CORE): $(RUST_CORE_SRC)
- $(call msg,RUSTC,,$@)
- $(Q)mkdir -p $(@D)
- +$(Q)$(RUSTC_BPF) -A warnings --edition 2024 --crate-name core --out-dir $(@D) $<
- +$(Q)echo '#![feature(compiler_builtins)] #![compiler_builtins] #![no_std]' | \
- $(RUSTC_BPF) -A warnings --crate-name compiler_builtins --out-dir $(@D) -
-endif
-
# Generated test list headers
define gen_tests_hdr
@@ -468,7 +458,7 @@ RUNNER_PREREQS := $(INCLUDE_DIR)/vmlinux.h $(BPFOBJ) $(BPFTOOL) \
$(VERIFY_SIG_HDR) $(PRIVATE_KEY) $(VERIFICATION_CERT) \
$(LIBARENA_SKEL) $(LIBARENA_ASAN_SKEL) \
prog_tests/tests.h map_tests/tests.h \
- $(RUNNER_OBJS) $(RUST_CORE)
+ $(RUNNER_OBJS)
# Runtime fixtures for each test_progs flavor.
RUNNER_EXTRA_FILES := $(OUTPUT)/urandom_read \
diff --git a/tools/testing/selftests/bpf/Makefile.buildvars b/tools/testing/selftests/bpf/Makefile.buildvars
index ff3476bc40a4..d2a0c0031b87 100644
--- a/tools/testing/selftests/bpf/Makefile.buildvars
+++ b/tools/testing/selftests/bpf/Makefile.buildvars
@@ -109,31 +109,6 @@ HOST_INCLUDE_DIR := $(INCLUDE_DIR)
endif
RESOLVE_BTFIDS := $(HOST_BUILD_DIR)/resolve_btfids/resolve_btfids
-# Programs in Rust are built by upstream rustc. It has no prebuilt core for
-# the bpf target, so it has to come with the source of core:
-# rustup component add rust-src
-# core is built as edition 2024, which it is since rustc 1.87.
-# rustc emits LLVM bitcode and clang makes the object of it, so clang has to be
-# 23 or newer and not older than LLVM of rustc.
-# Otherwise RUST_CORE is empty and the tests are skipped.
-RUSTC ?= rustc
-RUST_CORE_SRC := $(wildcard $(shell $(RUSTC) --print sysroot 2>/dev/null)$\
- /lib/rustlib/src/rust/library/core/src/lib.rs)
-ifneq ($(RUST_CORE_SRC),)
-ifeq ($(shell { clang=$$(echo __clang_major__ | $(CLANG) -E -P -x c -) && \
- llvm=$$($(srctree)/scripts/rustc-llvm-version.sh $(RUSTC)) && \
- [ $$($(srctree)/scripts/rustc-version.sh $(RUSTC)) -ge 108700 ] && \
- [ $$clang -ge 23 ] && [ $$clang -ge $$((llvm / 10000)) ]; } \
- 2>/dev/null && echo y),y)
-RUST_CORE := $(BUILD_DIR)/rust/libcore.rlib
-endif
-endif
-# RUSTC_BOOTSTRAP=1 is to build core with a stable rustc, like the kernel does.
-# panic=abort is a stop gap until panic=unwind is supported.
-RUSTC_BPF = RUSTC_BOOTSTRAP=1 $(RUSTC) -O -C panic=abort --crate-type rlib \
- --target $(if $(IS_LITTLE_ENDIAN),bpfel,bpfeb)-unknown-none \
- -L $(dir $(RUST_CORE))
-
DEFAULT_BPFTOOL := $(HOST_SCRATCH_DIR)/sbin/bpftool
ifneq ($(CROSS_COMPILE),)
CROSS_BPFTOOL := $(SCRATCH_DIR)/sbin/bpftool
diff --git a/tools/testing/selftests/bpf/Makefile.skel b/tools/testing/selftests/bpf/Makefile.skel
index 6570c48f92b0..e8699903feef 100644
--- a/tools/testing/selftests/bpf/Makefile.skel
+++ b/tools/testing/selftests/bpf/Makefile.skel
@@ -20,8 +20,7 @@ ifneq ($(BPF_CC),)
BPF_SRCS := $(notdir $(wildcard progs/*.c))
BPF_OBJS := $(patsubst %.c,$(RDIR)/%.bpf.o,$(BPF_SRCS))
-SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c \
- data_in_arena_extern.c data_in_arena_nomap.c
+SKEL_BLACKLIST := btf__% test_pinning_invalid.c test_sk_assign.c
LINKED_SKELS := test_static_linked.skel.h linked_funcs.skel.h \
linked_vars.skel.h linked_maps.skel.h linked_arena.skel.h \
@@ -138,21 +137,6 @@ $(LINKED_SKELS_H): $(RDIR)/%.skel.h: $$(addprefix $(RDIR)/,$$($$*.skel.h-deps))
$(BPFTOOL) $(GEN_SKEL) | $(RDIR)
$(Q)$(cmd_bpf_link_skel)
-# Programs in Rust, see Makefile.buildvars. No skeletons: the objects may be absent.
-ifneq ($(RUST_CORE),)
-ifeq ($(BPF_CC),$(CLANG))
-RUST_OBJS := $(patsubst progs/%.rs,$(RDIR)/%.bpf.o,$(wildcard progs/*.rs))
-BPF_OBJS += $(RUST_OBJS)
-
-$(RUST_OBJS): $(RDIR)/%.bpf.o: progs/%.rs $(RUST_CORE) | $(RDIR)
- $(call msg,RUSTC,$(BINARY),$@)
- +$(Q)$(RUSTC_BPF) --edition 2021 -C debuginfo=2 --emit=llvm-bc \
- $(patsubst -mcpu=%,-C target-cpu=%,$(filter -mcpu=%,$(BPF_CC_FLAGS))) \
- -o $(dir $(RUST_CORE))$(BINARY)-$*.bc $< && \
- $(BPF_CC) $(BPF_CC_FLAGS) -c $(dir $(RUST_CORE))$(BINARY)-$*.bc -o $@ $(call skip_on_fail,BPF)
-endif
-endif
-
# Programs in C++. No skeletons: gcc doesn't build them.
ifeq ($(BPF_CC),$(CLANG))
CXX_OBJS := $(patsubst progs/%.cpp,$(RDIR)/%.bpf.o,$(wildcard progs/*.cpp))
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_scalar_blinded.c b/tools/testing/selftests/bpf/prog_tests/arena_scalar_blinded.c
deleted file mode 100644
index 2ac2e9a652fe..000000000000
--- a/tools/testing/selftests/bpf/prog_tests/arena_scalar_blinded.c
+++ /dev/null
@@ -1,21 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <test_progs.h>
-#include "sysctl_helpers.h"
-#include "verifier_arena_scalar.skel.h"
-
-/* The same tests with constants of the programs blinded */
-void serial_test_arena_scalar_blinded(void)
-{
- const char *harden = "/proc/sys/net/core/bpf_jit_harden";
- char old[16] = {};
-
- if (!is_jit_enabled()) {
- test__skip();
- return;
- }
- if (sysctl_set_or_fail(harden, old, "2"))
- return;
- RUN_TESTS(verifier_arena_scalar);
- sysctl_set_or_fail(harden, NULL, old);
-}
diff --git a/tools/testing/selftests/bpf/prog_tests/data_in_arena.c b/tools/testing/selftests/bpf/prog_tests/data_in_arena.c
deleted file mode 100644
index cb1023507c01..000000000000
--- a/tools/testing/selftests/bpf/prog_tests/data_in_arena.c
+++ /dev/null
@@ -1,216 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <test_progs.h>
-#include "data_in_arena.skel.h"
-#include "data_in_arena_decl.skel.h"
-#include "data_in_arena_fail.skel.h"
-
-static int run_prog(struct bpf_program *prog)
-{
- LIBBPF_OPTS(bpf_test_run_opts, topts);
-
- if (!ASSERT_OK(bpf_prog_test_run_opts(bpf_program__fd(prog), &topts), "test_run"))
- return -1;
- return topts.retval;
-}
-
-static void run(struct data_in_arena *skel, int counter)
-{
- int i;
-
- ASSERT_EQ(run_prog(skel->progs.use_data), counter + 7 + 1 + 2, "retval");
- ASSERT_EQ(skel->bss->sum, counter + 7 + 1 + 2, "sum");
- ASSERT_EQ(skel->data->counter, counter + 1, "counter");
- ASSERT_EQ(skel->data->pair[1], 5, "pair[1]");
- for (i = 0; i < 4; i++)
- ASSERT_EQ(skel->bss->table[i], 10 * (i + 1) + i, "table");
-}
-
-static void test_in_arena(void)
-{
- struct bpf_map_info info = {};
- struct data_in_arena *skel;
- __u32 len = sizeof(info);
- struct bpf_map *arena;
- size_t sz;
-
- skel = data_in_arena__open();
- if (!ASSERT_OK_PTR(skel, "open"))
- return;
-
- arena = bpf_object__find_map_by_name(skel->obj, "arena");
- if (!ASSERT_OK_PTR(arena, "arena"))
- goto out;
- ASSERT_EQ(bpf_map__type(arena), BPF_MAP_TYPE_ARENA, "arena type");
- ASSERT_EQ(bpf_map__max_entries(arena), 1, "arena pages");
- ASSERT_OK(bpf_map__set_max_entries(arena, 8), "arena resize");
- ASSERT_FALSE(bpf_map__autocreate(skel->maps.data), "data autocreate");
- ASSERT_FALSE(bpf_map__autocreate(skel->maps.bss), "bss autocreate");
- ASSERT_FALSE(bpf_map__autocreate(skel->maps.rodata), "rodata autocreate");
- ASSERT_EQ(bpf_map__set_autocreate(skel->maps.data, true), -EOPNOTSUPP, "set_autocreate");
- ASSERT_EQ(bpf_map__set_value_size(skel->maps.bss, 4096), -EOPNOTSUPP, "set_value_size");
- ASSERT_EQ(bpf_map__initial_value(skel->maps.data, &sz), skel->data, "initial_value");
- ASSERT_EQ(sz, sizeof(*skel->data), "initial_value size");
-
- /* initial values are set the usual way */
- skel->data->counter = 100;
-
- if (!ASSERT_OK(data_in_arena__load(skel), "load"))
- goto out;
- /* there are no maps behind the sections */
- ASSERT_ERR(bpf_map_get_info_by_fd(bpf_map__fd(skel->maps.data), &info, &len), "data map");
- ASSERT_ERR(bpf_map_get_info_by_fd(bpf_map__fd(skel->maps.bss), &info, &len), "bss map");
- ASSERT_ERR(bpf_map_get_info_by_fd(bpf_map__fd(skel->maps.rodata), &info, &len),
- "rodata map");
- run(skel, 100);
-
- /* pointers to data next to pointers to functions */
- ASSERT_EQ(run_prog(skel->progs.use_ops), 42 + 1 + 'e', "use_ops");
- ASSERT_EQ(skel->data->counter, 102, "counter");
-
- /* alignment of sections and pointers to data in data */
- ASSERT_EQ((unsigned long)&skel->bss->aligned64 % 64, 0, "alignment");
- ASSERT_EQ(run_prog(skel->progs.use_ptrs), 0, "use_ptrs");
- ASSERT_EQ(skel->data->x, 43, "x");
- ASSERT_EQ(skel->bss->aligned64.v[7], 7, "aligned64");
- ASSERT_EQ(skel->data->px, &skel->data->x, "px");
-
- /* format strings of bpf_printk() and BPF_SNPRINTF() */
- ASSERT_EQ(run_prog(skel->progs.use_printk), sizeof("43-7"), "use_printk");
- ASSERT_STREQ(skel->bss->out, "43-7", "out");
-out:
- data_in_arena__destroy(skel);
-}
-
-/* The object has an arena map and __arena variables */
-static void test_declared_arena(void)
-{
- struct data_in_arena_decl *skel;
- struct bpf_map *map;
- int arenas = 0;
-
- skel = data_in_arena_decl__open();
- if (!ASSERT_OK_PTR(skel, "open"))
- return;
- bpf_object__for_each_map(map, skel->obj)
- arenas += bpf_map__type(map) == BPF_MAP_TYPE_ARENA;
- ASSERT_EQ(arenas, 1, "no second arena");
- skel->data->counter = 6;
- if (!ASSERT_OK(data_in_arena_decl__load(skel), "load"))
- goto out;
- ASSERT_EQ(run_prog(skel->progs.use_data), 6 + 7 + 11, "retval");
- ASSERT_EQ(run_prog(skel->progs.use_data), 7 + 7 + 12, "retval");
- ASSERT_EQ(skel->bss->sum, 7 + 7 + 12, "sum");
- ASSERT_EQ(skel->data->counter, 8, "counter");
-out:
- data_in_arena_decl__destroy(skel);
-}
-
-/* A pointer in data that can't be made an address of arena fails the load */
-static void test_ptr_to_map(void)
-{
- struct data_in_arena_fail *skel;
-
- skel = data_in_arena_fail__open();
- if (!ASSERT_OK_PTR(skel, "open"))
- return;
- ASSERT_ERR(data_in_arena_fail__load(skel), "load");
- data_in_arena_fail__destroy(skel);
-}
-
-/* So does a pointer to a variable of the kernel. There is no skeleton: the open fails. */
-static void test_ptr_to_extern(void)
-{
- struct bpf_object *obj;
-
- obj = bpf_object__open_file("./data_in_arena_extern.bpf.o", NULL);
- if (!ASSERT_ERR_PTR(obj, "open"))
- bpf_object__close(obj);
-}
-
-/* __arena variables and no arena map. There is no skeleton: the open fails. */
-static void test_arena_var_no_map(void)
-{
- struct bpf_object *obj;
-
- obj = bpf_object__open_file("./data_in_arena_nomap.bpf.o", NULL);
- if (!ASSERT_ERR_PTR(obj, "open"))
- bpf_object__close(obj);
-}
-
-/* The address of an arena that libbpf doesn't create is not known. No pointers in data then. */
-static void test_not_my_arena(bool pin)
-{
- LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
- struct data_in_arena *skel;
- struct bpf_map *arena;
- int fd = -1;
-
- skel = data_in_arena__open();
- if (!ASSERT_OK_PTR(skel, "open"))
- return;
- arena = bpf_object__find_map_by_name(skel->obj, "arena");
- if (!ASSERT_OK_PTR(arena, "arena"))
- goto out;
- if (pin) {
- ASSERT_OK(bpf_map__set_pin_path(arena, "/sys/fs/bpf/data_in_arena"), "pin_path");
- } else {
- fd = bpf_map_create(BPF_MAP_TYPE_ARENA, "arena", 0, 0, 1, &opts);
- if (!ASSERT_GE(fd, 0, "map_create"))
- goto out;
- ASSERT_OK(bpf_map__reuse_fd(arena, fd), "reuse_fd");
- }
- ASSERT_EQ(data_in_arena__load(skel), -ENOTSUP, "load");
-out:
- if (fd >= 0)
- close(fd);
- data_in_arena__destroy(skel);
-}
-
-/* The object is there if rustc and clang can build it, see Makefile.buildvars */
-static void test_rust(void)
-{
- const char *file = "./data_in_arena_rust.bpf.o";
- struct bpf_object *obj;
-
- if (access(file, R_OK)) {
- test__skip();
- return;
- }
- obj = bpf_object__open_file(file, NULL);
- if (!ASSERT_OK_PTR(obj, "open"))
- return;
- if (!ASSERT_OK(bpf_object__load(obj), "load"))
- goto out;
- /* libbpf goes on without BTF when the kernel doesn't take it */
- ASSERT_GE(bpf_object__btf_fd(obj), 0, "btf_fd");
- ASSERT_EQ(run_prog(bpf_object__find_program_by_name(obj, "list_in_data")),
- 100 + 20 + 3, "retval");
-out:
- bpf_object__close(obj);
-}
-
-void test_data_in_arena(void)
-{
-#if !defined(__x86_64__) && !defined(__aarch64__)
- /* other JITs don't take BPF_F_ARENA_SCALAR */
- test__skip();
- return;
-#endif
- if (test__start_subtest("arena"))
- test_in_arena();
- if (test__start_subtest("declared_arena"))
- test_declared_arena();
- if (test__start_subtest("ptr_to_map"))
- test_ptr_to_map();
- if (test__start_subtest("ptr_to_extern"))
- test_ptr_to_extern();
- if (test__start_subtest("arena_var_no_map"))
- test_arena_var_no_map();
- if (test__start_subtest("pinned_arena"))
- test_not_my_arena(true);
- if (test__start_subtest("reused_arena"))
- test_not_my_arena(false);
- if (test__start_subtest("rust"))
- test_rust();
-}
diff --git a/tools/testing/selftests/bpf/prog_tests/jit_insns.c b/tools/testing/selftests/bpf/prog_tests/jit_insns.c
index dabefdb8a353..52bffe27199b 100644
--- a/tools/testing/selftests/bpf/prog_tests/jit_insns.c
+++ b/tools/testing/selftests/bpf/prog_tests/jit_insns.c
@@ -2882,6 +2882,63 @@ static void test_convergence(void)
}
/*
+ * On x86-64 a backward jump sized from its own previous-pass address grew
+ * here while others shrank, so two passes gave the same image length with
+ * jumps still changing. The fillers are 7 and 2 bytes of native code, the
+ * conditional jumps are never taken.
+ */
+static void test_same_len_not_converged(void)
+{
+ struct bpf_insn insns[] = {
+ BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, IN0),
+ BPF_LDX_MEM(BPF_DW, BPF_REG_4, BPF_REG_1, IN1),
+ BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+ BPF_JMP_IMM(BPF_JA, 0, 0, 29),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, 20),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, -3),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, 22),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, 2),
+ BPF_MOV32_IMM(BPF_REG_2, 0),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, 23),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV32_IMM(BPF_REG_2, 0),
+ BPF_JMP_IMM(BPF_JA, 0, 0, -22),
+ BPF_MOV64_IMM(BPF_REG_2, -1),
+ BPF_MOV32_IMM(BPF_REG_2, 0),
+ BPF_MOV32_IMM(BPF_REG_2, 0),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, 3),
+ BPF_MOV32_IMM(BPF_REG_2, 0),
+ BPF_JMP32_REG(BPF_JEQ, BPF_REG_0, BPF_REG_4, 2),
+ BPF_JMP_IMM(BPF_JA, 0, 0, -22),
+ BPF_MOV32_IMM(BPF_REG_2, 0),
+ BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 10),
+ BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_0, RES),
+ BPF_MOV64_IMM(BPF_REG_0, 0),
+ BPF_EXIT_INSN(),
+ };
+ int fd;
+
+ fd = load_prog(insns, ARRAY_SIZE(insns), "same len not converged");
+ if (fd < 0)
+ return;
+ ASSERT_GT(jited_len(fd), 0, "same len not converged jited");
+ prog_run(fd, 1, 2, 11, "same len not converged");
+ close(fd);
+}
+
+/*
* Calls into a subprogram with all five arguments, which clobbers a subset
* of r6-r9 and uses the deepest stack slot the budget leaves, directly or
* through another one clobbering the rest, so the JIT has to save and
@@ -3087,6 +3144,8 @@ void test_jit_insns(void)
test_max_jmp();
if (test__start_subtest("convergence"))
test_convergence();
+ if (test__start_subtest("same_len_not_converged"))
+ test_same_len_not_converged();
if (test__start_subtest("calls"))
test_calls();
if (test__start_subtest("helper_calls"))
diff --git a/tools/testing/selftests/bpf/prog_tests/map_kptr.c b/tools/testing/selftests/bpf/prog_tests/map_kptr.c
index 17e707dddda8..f103f8cd458b 100644
--- a/tools/testing/selftests/bpf/prog_tests/map_kptr.c
+++ b/tools/testing/selftests/bpf/prog_tests/map_kptr.c
@@ -225,6 +225,11 @@ void serial_test_map_kptr(void)
/* Observe refcount dropping to 1 on map release. */
test_map_kptr_success(true);
+
+ /* The next test counts the same references */
+ ASSERT_OK(kern_sync_rcu_tasks_trace(skel), "sync rcu_tasks_trace");
+ ASSERT_OK(kern_sync_rcu(), "sync rcu");
+ wait_for_map_release();
}
rcu_tasks_trace_gp__destroy(skel);
diff --git a/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c b/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
index de23a14b3d40..10b4ea913921 100644
--- a/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
+++ b/tools/testing/selftests/bpf/prog_tests/rcu_reentry.c
@@ -23,10 +23,17 @@ static bool have_tree_srcu(void)
return kallsyms_find("srcu_expedite_current", &addr) == 0;
}
+/* Arm the handler for this thread, then trigger call_rcu_tasks_trace(). */
+static noinline int arm_and_delete(struct rcu_reentry *skel, int pid, int map_fd, int *pidfd)
+{
+ skel->bss->target_pid = pid;
+ return bpf_map_delete_elem(map_fd, pidfd);
+}
+
void test_rcu_reentry(void)
{
struct rcu_reentry *skel;
- int err, pidfd = -1, map_fd;
+ int err, pidfd = -1, bad_fd = -1, map_fd;
cpu_set_t set, old_set;
bool affinity_saved;
__u64 val = 1;
@@ -63,9 +70,13 @@ void test_rcu_reentry(void)
if (!ASSERT_OK(err, "boot_create"))
goto restore;
- /* Arm the handler for this thread, then trigger call_rcu_tasks_trace(). */
- skel->bss->target_pid = syscall(__NR_gettid);
- err = bpf_map_delete_elem(map_fd, &pidfd);
+ /*
+ * The handler fires at the first rcu_segcblist_enqueue() in this thread, and it deletes
+ * the element. A page fault can get there too, when the file system reads the page.
+ * Go through the same code first, without a pid, to bring its text and .bss in.
+ */
+ arm_and_delete(skel, 0, map_fd, &bad_fd);
+ err = arm_and_delete(skel, syscall(__NR_gettid), map_fd, &pidfd);
if (!ASSERT_OK(err, "boot_delete"))
goto restore;
diff --git a/tools/testing/selftests/bpf/prog_tests/send_signal.c b/tools/testing/selftests/bpf/prog_tests/send_signal.c
index 7ac4d5a488aa..ecec2ee8b19a 100644
--- a/tools/testing/selftests/bpf/prog_tests/send_signal.c
+++ b/tools/testing/selftests/bpf/prog_tests/send_signal.c
@@ -261,6 +261,110 @@ static void test_send_signal_nmi(bool signal_thread, bool remote)
test_send_signal_common(&attr, signal_thread, remote);
}
+static void test_send_signal_kworker(void)
+{
+ struct test_send_signal_kern *skel;
+ int pipe_c2p[2], pipe_p2c[2];
+ struct sigaction sa = {};
+ char buf[256];
+ int err = -1;
+ pid_t pid;
+
+ if (!ASSERT_OK(pipe(pipe_c2p), "pipe_c2p"))
+ return;
+
+ if (!ASSERT_OK(pipe(pipe_p2c), "pipe_p2c")) {
+ close(pipe_c2p[0]);
+ close(pipe_c2p[1]);
+ return;
+ }
+
+ pid = fork();
+ if (!ASSERT_GE(pid, 0, "fork")) {
+ close(pipe_c2p[0]);
+ close(pipe_c2p[1]);
+ close(pipe_p2c[0]);
+ close(pipe_p2c[1]);
+ return;
+ }
+
+ if (pid == 0) {
+ /* install signal handler and notify parent */
+ sa.sa_sigaction = sigusr1_siginfo_handler;
+ sa.sa_flags = SA_RESTART | SA_SIGINFO;
+ ASSERT_NEQ(sigaction(SIGUSR1, &sa, NULL), -1, "sigaction");
+
+ close(pipe_c2p[0]); /* close read */
+ close(pipe_p2c[1]); /* close write */
+
+ /* notify parent signal handler is installed */
+ ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write");
+
+ /* make sure parent enabled bpf program to send_signal */
+ ASSERT_EQ(read(pipe_p2c[0], buf, 1), 1, "pipe_read");
+
+ /*
+ * The signal is sent from workqueue context, so nothing has
+ * to be triggered from here.
+ */
+ while (!sigusr1_received)
+ sleep(1);
+
+ buf[0] = sigusr1_received;
+
+ ASSERT_EQ(sigusr1_received, 8, "sigusr1_received");
+ ASSERT_EQ(write(pipe_c2p[1], buf, 1), 1, "pipe_write");
+
+ close(pipe_c2p[1]);
+ close(pipe_p2c[0]);
+ exit(0);
+ }
+
+ close(pipe_c2p[1]); /* close write */
+ close(pipe_p2c[0]); /* close read */
+
+ skel = test_send_signal_kern__open_and_load();
+ if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+ goto skel_open_load_failure;
+
+ /* wait until child signal handler installed */
+ ASSERT_EQ(read(pipe_c2p[0], buf, 1), 1, "pipe_read");
+
+ /* pid == 0 keeps the other programs of this skeleton inactive */
+ skel->bss->kworker_test = 1;
+ skel->bss->pid = 0;
+ skel->bss->sig = SIGUSR1;
+ skel->bss->target_pid = pid;
+
+ err = test_send_signal_kern__attach(skel);
+ if (!ASSERT_OK(err, "skel_attach")) {
+ err = -1;
+ goto destroy_skel;
+ }
+
+ /* notify child that bpf program can send_signal now */
+ ASSERT_EQ(write(pipe_p2c[1], buf, 1), 1, "pipe_write");
+
+ /* wait for result, workqueues run on their own */
+ err = read_with_timeout(pipe_c2p[0], buf, 1, 10 * 1000 * 1000);
+ if (!ASSERT_GT(err, 0, "reading pipe"))
+ goto destroy_skel;
+
+ ASSERT_EQ(buf[0], 8, "incorrect result");
+
+destroy_skel:
+ test_send_signal_kern__destroy(skel);
+skel_open_load_failure:
+ close(pipe_c2p[0]);
+ close(pipe_p2c[1]);
+ /*
+ * Child is either about to exit cleanly or stuck in case of errors.
+ * Nudge it to exit.
+ */
+ kill(pid, SIGKILL);
+ wait(NULL);
+}
+
void test_send_signal(void)
{
if (test__start_subtest("send_signal_tracepoint"))
@@ -289,4 +393,6 @@ void test_send_signal(void)
test_send_signal_perf(true, true);
if (test__start_subtest("send_signal_nmi_thread_remote"))
test_send_signal_nmi(true, true);
+ if (test__start_subtest("send_signal_kworker"))
+ test_send_signal_kworker();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
index 461ded722351..9b646d134a45 100644
--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_assoc.c
@@ -136,7 +136,8 @@ out:
struct_ops_assoc_in_timer__destroy(skel);
}
-static void test_st_ops_assoc_in_timer_no_uref(void)
+static void run_st_ops_assoc_in_timer_no_uref(int timer_ns, int wait_map_free,
+ int spin_loops)
{
struct struct_ops_assoc_in_timer *skel = NULL;
struct bpf_link *link;
@@ -148,7 +149,8 @@ static void test_st_ops_assoc_in_timer_no_uref(void)
err = bpf_program__assoc_struct_ops(skel->progs.syscall_prog,
skel->maps.st_ops_map, NULL);
- ASSERT_OK(err, "bpf_program__assoc_struct_ops");
+ if (!ASSERT_OK(err, "bpf_program__assoc_struct_ops"))
+ goto out;
link = bpf_map__attach_struct_ops(skel->maps.st_ops_map);
if (!ASSERT_OK_PTR(link, "bpf_map__attach_struct_ops"))
@@ -157,12 +159,18 @@ static void test_st_ops_assoc_in_timer_no_uref(void)
/*
* Run .test_1 by calling kfunc bpf_kfunc_multi_st_ops_test_1_prog_arg() and checks
* the return value. .test_1 will also schedule timer_cb that runs .test_1 again.
- * timer_cb will run 500ms after syscall_prog runs, when the user space no longer
- * holds a reference to st_ops_map.
+ * timer_cb will run timer_ns after syscall_prog runs and, with wait_map_free, every
+ * timer_ns after that until the map is freed. With 500ms, it first runs when the
+ * user space no longer holds a reference to st_ops_map.
*/
- skel->bss->timer_ns = 500000000;
+ skel->bss->timer_ns = timer_ns;
+ skel->bss->wait_map_free = wait_map_free;
+ skel->bss->spin_loops = spin_loops;
err = bpf_prog_test_run_opts(bpf_program__fd(skel->progs.syscall_prog), NULL);
- ASSERT_OK(err, "bpf_prog_test_run_opts");
+ if (!ASSERT_OK(err, "bpf_prog_test_run_opts")) {
+ bpf_link__destroy(link);
+ goto out;
+ }
/* Detach and close struct_ops map to cause it to be freed */
bpf_link__destroy(link);
@@ -178,6 +186,21 @@ out:
struct_ops_assoc_in_timer__destroy(skel);
}
+static void test_st_ops_assoc_in_timer_no_uref(void)
+{
+ run_st_ops_assoc_in_timer_no_uref(500000000, 1, 0);
+}
+
+/*
+ * Keep calling .test_1 through the association from the timer callback, and
+ * stay in it for a while each time, while the map is being freed. The map
+ * must not be freed under a running .test_1.
+ */
+static void test_st_ops_assoc_in_timer_free(void)
+{
+ run_st_ops_assoc_in_timer_no_uref(0, 1, 1 << 20);
+}
+
void test_struct_ops_assoc(void)
{
if (test__start_subtest("st_ops_assoc"))
@@ -188,4 +211,6 @@ void test_struct_ops_assoc(void)
test_st_ops_assoc_in_timer();
if (test__start_subtest("st_ops_assoc_in_timer_no_uref"))
test_st_ops_assoc_in_timer_no_uref();
+ if (test__start_subtest("st_ops_assoc_in_timer_free"))
+ test_st_ops_assoc_in_timer_free();
}
diff --git a/tools/testing/selftests/bpf/prog_tests/verifier.c b/tools/testing/selftests/bpf/prog_tests/verifier.c
index 04740ba37e91..6a494c381266 100644
--- a/tools/testing/selftests/bpf/prog_tests/verifier.c
+++ b/tools/testing/selftests/bpf/prog_tests/verifier.c
@@ -13,7 +13,6 @@
#include "verifier_and.skel.h"
#include "verifier_arena.skel.h"
#include "verifier_arena_large.skel.h"
-#include "verifier_arena_scalar.skel.h"
#include "verifier_arena_globals1.skel.h"
#include "verifier_arena_globals2.skel.h"
#include "verifier_array_access.skel.h"
@@ -207,7 +206,6 @@ void test_verifier_align(void) { RUN(verifier_align); }
void test_verifier_and(void) { RUN(verifier_and); }
void test_verifier_arena(void) { RUN(verifier_arena); }
void test_verifier_arena_large(void) { RUN(verifier_arena_large); }
-void test_verifier_arena_scalar(void) { RUN(verifier_arena_scalar); }
void test_verifier_arena_globals1(void) { RUN(verifier_arena_globals1); }
void test_verifier_arena_globals2(void) { RUN(verifier_arena_globals2); }
void test_verifier_basic_stack(void) { RUN(verifier_basic_stack); }
diff --git a/tools/testing/selftests/bpf/progs/arena_noarena.c b/tools/testing/selftests/bpf/progs/arena_noarena.c
index edbd40cd5be8..1f46e5aa403b 100644
--- a/tools/testing/selftests/bpf/progs/arena_noarena.c
+++ b/tools/testing/selftests/bpf/progs/arena_noarena.c
@@ -21,19 +21,4 @@ int arena_arg_no_arena(void *ctx)
return 0;
}
-SEC("syscall")
-__flag(BPF_F_ARENA_SCALAR)
-__arch_x86_64
-__arch_arm64
-__description("arena_scalar: no arena, no access through a number")
-__failure __msg("R6 invalid mem access 'scalar'")
-__naked void no_arena(void)
-{
- asm volatile (" \
- r6 = 0x100000000000 ll; \
- r0 = *(u64 *)(r6 + 0); \
- exit; \
-" ::: __clobber_all);
-}
-
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c b/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c
index a980a3a399d3..f4fb8f81afa7 100644
--- a/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c
+++ b/tools/testing/selftests/bpf/progs/btf__verifier_stack_arg_order.c
@@ -26,6 +26,11 @@ void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g)
{
}
+int callback_6args(long a, long b, long c, long d, long e, long f)
+{
+ return a + b + c + d + e + f;
+}
+
#else
int subprog_bad_order_6args(void)
@@ -47,4 +52,9 @@ void subprog_bad_ptr_7args(void)
{
}
+int callback_6args(void)
+{
+ return 0;
+}
+
#endif
diff --git a/tools/testing/selftests/bpf/progs/data_in_arena.c b/tools/testing/selftests/bpf/progs/data_in_arena.c
deleted file mode 100644
index 18f38103c0fc..000000000000
--- a/tools/testing/selftests/bpf/progs/data_in_arena.c
+++ /dev/null
@@ -1,112 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/bpf.h>
-#include <bpf/bpf_helpers.h>
-
-/* global data of the object is in arena */
-char data_in_arena SEC(".arena.data");
-
-int counter = 5;
-long pair[2] = { 1, 2 };
-int x = 42;
-long sum;
-long table[4];
-struct {
- long v[8];
-} aligned64 __attribute__((aligned(64)));
-const volatile int ro = 7;
-const volatile long ro_table[4] = { 10, 20, 30, 40 };
-
-/* const strings stay in a map for helpers and kfuncs, a copy of them is in arena */
-const char hello[] SEC(".rodata.str.hello") = "hello";
-
-/* pointers to data that are stored in data */
-int *px = &x;
-const char *str = hello;
-int *const volatile cpx SEC(".data.rel.ro") = &x;
-
-SEC("syscall")
-int use_data(void *ctx)
-{
- int i;
-
- for (i = 0; i < 4; i++)
- table[i] = ro_table[i] + i;
- sum = counter + ro + pair[0] + pair[1];
- counter++;
- __sync_fetch_and_add(&pair[1], 3);
- return sum;
-}
-
-typedef int (*op_fn)(int);
-
-static __noinline int add1(int v)
-{
- return v + 1;
-}
-
-/*
- * Pointers to functions and to data in read-only data of a program with callx.
- * Volatile, so that the compiler doesn't replace the pointers with what
- * they point to.
- */
-static const volatile struct {
- op_fn fn;
- int *data;
- const char *name;
-} ops SEC(".data.rel.ro") = { add1, &x, hello };
-
-SEC("syscall")
-int use_ops(void *ctx)
-{
- /* a program has the arena when its code refers to it */
- counter++;
-#ifdef __clang__
- return ops.fn(*ops.data) + ops.name[1];
-#else
- /* gcc doesn't support indirect calls */
- return add1(*ops.data) + ops.name[1];
-#endif
-}
-
-SEC("syscall")
-int use_ptrs(void *ctx)
-{
- unsigned long addr = (unsigned long)&aligned64;
- char local[4] = "abc";
-
- /* hide the address from the compiler, it knows that '& 63' is 0 */
- asm volatile ("" : "+r"(addr));
- if (addr & 63)
- return 1;
- aligned64.v[7] = 7;
- if (*px != 42)
- return 2;
- *px = 43;
- if (x != 43 || *cpx != 43)
- return 3;
- if (str[0] != 'h' || str[4] != 'o' || str[5])
- return 4;
- /* the literal is in a map */
- if (bpf_strncmp(local, sizeof(local), "abc"))
- return 5;
- return 0;
-}
-
-char out[16];
-
-/* format strings are in a map */
-SEC("syscall")
-int use_printk(void *ctx)
-{
- char buf[sizeof(out)];
- int i, n;
-
- bpf_printk("counter %d", counter);
- n = BPF_SNPRINTF(buf, sizeof(buf), "%d-%d", x, ro);
- for (i = 0; i < sizeof(out); i++)
- out[i] = buf[i];
- return n;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/data_in_arena_decl.c b/tools/testing/selftests/bpf/progs/data_in_arena_decl.c
deleted file mode 100644
index 01bc4fea8f0c..000000000000
--- a/tools/testing/selftests/bpf/progs/data_in_arena_decl.c
+++ /dev/null
@@ -1,37 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#define BPF_NO_KFUNC_PROTOTYPES
-#include <vmlinux.h>
-#include <bpf/bpf_helpers.h>
-#include "bpf_experimental.h"
-#include <bpf_arena_common.h>
-
-struct {
- __uint(type, BPF_MAP_TYPE_ARENA);
- __uint(map_flags, BPF_F_MMAPABLE);
- __uint(max_entries, 4);
-} arena SEC(".maps");
-
-/* global data of the object is in arena */
-char data_in_arena SEC(".arena.data");
-
-int counter = 5;
-long sum;
-const volatile int ro = 7;
-
-#if defined(__BPF_FEATURE_ADDR_SPACE_CAST)
-int __arena avar = 11;
-#else
-int avar = 11;
-#endif
-
-SEC("syscall")
-int use_data(void *ctx)
-{
- sum = counter + ro + avar;
- counter++;
- avar++;
- return sum;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/data_in_arena_extern.c b/tools/testing/selftests/bpf/progs/data_in_arena_extern.c
deleted file mode 100644
index dc1ad5ca3bdd..000000000000
--- a/tools/testing/selftests/bpf/progs/data_in_arena_extern.c
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/bpf.h>
-#include <bpf/bpf_helpers.h>
-
-/* global data of the object is in arena */
-char data_in_arena SEC(".arena.data");
-
-extern const int bpf_prog_active __ksym;
-
-/* the variable of the kernel is not in arena */
-const void *kp = &bpf_prog_active;
-
-SEC("syscall")
-int ptr_to_extern(void *ctx)
-{
- return *(int *)kp;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/data_in_arena_fail.c b/tools/testing/selftests/bpf/progs/data_in_arena_fail.c
deleted file mode 100644
index ad8afe9afc96..000000000000
--- a/tools/testing/selftests/bpf/progs/data_in_arena_fail.c
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/bpf.h>
-#include <bpf/bpf_helpers.h>
-
-/* global data of the object is in arena */
-char data_in_arena SEC(".arena.data");
-
-int x = 42;
-/* the table is read-only data with pointers. It's not in arena. */
-int *const volatile tbl[1] SEC(".data.rel.ro") = { &x };
-int *const volatile *pp = tbl;
-
-SEC("syscall")
-int ptr_to_map(void *ctx)
-{
- return **pp;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/data_in_arena_nomap.c b/tools/testing/selftests/bpf/progs/data_in_arena_nomap.c
deleted file mode 100644
index ccf66321eb36..000000000000
--- a/tools/testing/selftests/bpf/progs/data_in_arena_nomap.c
+++ /dev/null
@@ -1,20 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <vmlinux.h>
-#include <bpf/bpf_helpers.h>
-#include "bpf_arena_common.h"
-
-/* global data of the object is in arena */
-char data_in_arena SEC(".arena.data");
-
-int counter = 5;
-/* needs an arena map that is declared. The one that libbpf creates won't do. */
-int __arena_global avar = 11;
-
-SEC("syscall")
-int arena_var(void *ctx)
-{
- return avar + counter;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/data_in_arena_rust.rs b/tools/testing/selftests/bpf/progs/data_in_arena_rust.rs
deleted file mode 100644
index 3dd9d4208232..000000000000
--- a/tools/testing/selftests/bpf/progs/data_in_arena_rust.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-// Why .data, .bss and .rodata of a program in Rust are in arena.
-//
-// A reference in Rust is an address. It doesn't say what it points to and it
-// can be stored in data. The list below has a node in each of the sections.
-// The nodes are linked by references that are in the data:
-// IN_BSS.next is stored by the program,
-// IN_DATA.next is a relocation in .data against .rodata.
-// sum() loads the references back and reads the three nodes with the same insn.
-//
-// When the sections are array maps libbpf skips the relocation in .data, and
-// what sum() loads from a node is a number that can't be dereferenced:
-// R1 invalid mem access 'scalar'
-// In arena the address of a node is a number to begin with.
-
-#![no_std]
-#![no_main]
-
-// Tell libbpf to keep .data, .bss and .rodata in arena.
-#[used]
-#[link_section = ".arena.data"]
-static DATA_IN_ARENA: u8 = 0;
-
-#[used]
-#[link_section = "license"]
-static LICENSE: [u8; 4] = *b"GPL\0";
-
-// panic=abort is a stop gap until panic=unwind is supported.
-// Nothing here panics, so the handler is not a part of the program.
-#[panic_handler]
-fn panic(_info: &core::panic::PanicInfo) -> ! {
- loop {}
-}
-
-pub struct Node {
- val: u32,
- next: Option<&'static Node>,
-}
-
-// no_mangle makes them visible outside, so LLVM can't fold the list into a constant.
-#[no_mangle]
-static IN_RODATA: Node = Node { val: 3, next: None };
-#[no_mangle]
-static mut IN_DATA: Node = Node {
- val: 20,
- next: Some(&IN_RODATA),
-};
-#[no_mangle]
-static mut IN_BSS: Node = Node { val: 0, next: None };
-
-#[inline(never)]
-fn sum(mut node: Option<&Node>) -> u32 {
- let mut sum = 0;
- // The verifier wants a bound.
- for _ in 0..8 {
- let Some(n) = node else { break };
- sum += n.val;
- node = n.next;
- }
- sum
-}
-
-#[no_mangle]
-#[link_section = "syscall"]
-pub extern "C" fn list_in_data(_ctx: *mut u8) -> u32 {
- unsafe {
- let head = &mut *&raw mut IN_BSS;
- head.val = 100;
- head.next = Some(&*&raw const IN_DATA);
- sum(Some(head))
- }
-}
diff --git a/tools/testing/selftests/bpf/progs/iters_testmod.c b/tools/testing/selftests/bpf/progs/iters_testmod.c
index 145750bb64ef..8896335668b4 100644
--- a/tools/testing/selftests/bpf/progs/iters_testmod.c
+++ b/tools/testing/selftests/bpf/progs/iters_testmod.c
@@ -8,6 +8,44 @@
char _license[] SEC("license") = "GPL";
+struct bpf_iter_testmod_current {
+ u64 __opaque;
+};
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_independent_lifetime_after_next(const void *ctx)
+{
+ struct bpf_iter_testmod_current it;
+ struct task_struct *task;
+
+ bpf_iter_testmod_current_new(&it);
+ task = bpf_iter_testmod_current_next(&it);
+ if (!task)
+ goto out;
+
+ bpf_iter_testmod_current_next(&it);
+ bpf_kfunc_trusted_task_test(task);
+out:
+ bpf_iter_testmod_current_destroy(&it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_independent_lifetime_after_destroy(const void *ctx)
+{
+ struct bpf_iter_testmod_current it;
+ struct task_struct *task;
+
+ bpf_iter_testmod_current_new(&it);
+ task = bpf_iter_testmod_current_next(&it);
+ bpf_iter_testmod_current_destroy(&it);
+ if (task)
+ bpf_kfunc_trusted_task_test(task);
+ return 0;
+}
+
SEC("raw_tp/sys_enter")
__success
int iter_next_trusted(const void *ctx)
@@ -29,6 +67,298 @@ out:
}
SEC("raw_tp/sys_enter")
+__failure __msg("invalid mem access 'scalar'")
+int iter_next_trusted_after_destroy(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ bpf_iter_task_vma_destroy(&vma_it);
+ return vma_ptr->vm_start;
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("invalid mem access 'scalar'")
+int iter_next_trusted_after_next(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr, *next_vma_ptr;
+ u64 vm_start;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ next_vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!next_vma_ptr)
+ goto out;
+
+ vm_start = vma_ptr->vm_start;
+ bpf_iter_task_vma_destroy(&vma_it);
+ return vm_start;
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("invalid mem access 'scalar'")
+int iter_next_trusted_after_next_null(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr, *next_vma_ptr;
+ u64 vm_start;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ next_vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (next_vma_ptr)
+ goto out;
+
+ vm_start = vma_ptr->vm_start;
+ bpf_iter_task_vma_destroy(&vma_it);
+ return vm_start;
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("invalid mem access 'scalar'")
+int iter_next_trusted_field_after_destroy(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+ struct file *file_ptr;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ file_ptr = vma_ptr->vm_file;
+ bpf_iter_task_vma_destroy(&vma_it);
+ if (file_ptr)
+ return file_ptr->f_mode;
+ return 0;
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("invalid mem access 'scalar'")
+int iter_next_trusted_field_after_next(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr, *next_vma_ptr;
+ struct file *file_ptr;
+ u32 mode;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ file_ptr = vma_ptr->vm_file;
+ if (!file_ptr)
+ goto out;
+
+ next_vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!next_vma_ptr)
+ goto out;
+
+ mode = file_ptr->f_mode;
+ bpf_iter_task_vma_destroy(&vma_it);
+ return mode;
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("Leaking reference id=")
+int iter_next_file_dynptr_after_next(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+ struct bpf_dynptr dynptr;
+ struct file *file_ptr;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ file_ptr = vma_ptr->vm_file;
+ if (!file_ptr)
+ goto out;
+
+ bpf_dynptr_from_file(file_ptr, 0, &dynptr);
+ bpf_iter_task_vma_next(&vma_it);
+ bpf_dynptr_file_discard(&dynptr);
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__failure __msg("Leaking reference id=")
+int iter_next_file_dynptr_after_destroy(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+ struct bpf_dynptr dynptr;
+ struct file *file_ptr;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ file_ptr = vma_ptr->vm_file;
+ if (!file_ptr)
+ goto out;
+
+ bpf_dynptr_from_file(file_ptr, 0, &dynptr);
+ bpf_iter_task_vma_destroy(&vma_it);
+ bpf_dynptr_file_discard(&dynptr);
+ return 0;
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_file_dynptr_discard_before_advance(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+ struct bpf_dynptr dynptr;
+ struct file *file_ptr;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ file_ptr = vma_ptr->vm_file;
+ if (!file_ptr)
+ goto out;
+
+ bpf_dynptr_from_file(file_ptr, 0, &dynptr);
+ bpf_dynptr_file_discard(&dynptr);
+ bpf_iter_task_vma_next(&vma_it);
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_trusted_current_after_next(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (!vma_ptr)
+ goto out;
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (vma_ptr)
+ bpf_kfunc_trusted_vma_test(vma_ptr);
+out:
+ bpf_iter_task_vma_destroy(&vma_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_trusted_rcu_field_after_destroy(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+ struct mm_struct *mm_ptr;
+ struct file *file_ptr = NULL;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (vma_ptr) {
+ mm_ptr = vma_ptr->vm_mm;
+ /* exe_file has RCU protection independent of the iterator. */
+ if (mm_ptr)
+ file_ptr = mm_ptr->exe_file;
+ }
+
+ bpf_iter_task_vma_destroy(&vma_it);
+ if (file_ptr)
+ return file_ptr->f_mode;
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_trusted_rcu_field_after_next(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task_vma vma_it;
+ struct vm_area_struct *vma_ptr;
+ struct mm_struct *mm_ptr;
+ struct file *file_ptr = NULL;
+ u32 mode = 0;
+
+ bpf_iter_task_vma_new(&vma_it, cur_task, 0);
+
+ vma_ptr = bpf_iter_task_vma_next(&vma_it);
+ if (vma_ptr) {
+ mm_ptr = vma_ptr->vm_mm;
+ if (mm_ptr)
+ file_ptr = mm_ptr->exe_file;
+ }
+
+ bpf_iter_task_vma_next(&vma_it);
+ if (file_ptr)
+ mode = file_ptr->f_mode;
+ bpf_iter_task_vma_destroy(&vma_it);
+ return mode;
+}
+
+SEC("raw_tp/sys_enter")
__failure __msg("Possibly NULL pointer passed to trusted R1")
int iter_next_trusted_or_null(const void *ctx)
{
@@ -78,6 +408,46 @@ out:
}
SEC("raw_tp/sys_enter")
+__success
+int iter_next_rcu_after_destroy(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task task_it;
+ struct task_struct *task_ptr;
+
+ bpf_iter_task_new(&task_it, cur_task, 0);
+
+ task_ptr = bpf_iter_task_next(&task_it);
+ bpf_iter_task_destroy(&task_it);
+ if (!task_ptr)
+ return 0;
+
+ bpf_kfunc_rcu_task_test(task_ptr);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
+__success
+int iter_next_rcu_after_next(const void *ctx)
+{
+ struct task_struct *cur_task = bpf_get_current_task_btf();
+ struct bpf_iter_task task_it;
+ struct task_struct *task_ptr;
+
+ bpf_iter_task_new(&task_it, cur_task, 0);
+
+ task_ptr = bpf_iter_task_next(&task_it);
+ if (!task_ptr)
+ goto out;
+
+ bpf_iter_task_next(&task_it);
+ bpf_kfunc_rcu_task_test(task_ptr);
+out:
+ bpf_iter_task_destroy(&task_it);
+ return 0;
+}
+
+SEC("raw_tp/sys_enter")
__failure __msg("Possibly NULL pointer passed to trusted R1")
int iter_next_rcu_or_null(const void *ctx)
{
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
index 0bed49e9f217..47a44c8e8b11 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_assoc_in_timer.c
@@ -25,6 +25,13 @@ int test_err;
int timer_ns;
int timer_test_1_ret;
int timer_cb_run;
+int wait_map_free;
+int spin_loops;
+
+static int spin(u32 i, void *ctx)
+{
+ return 0;
+}
__noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer)
{
@@ -34,6 +41,12 @@ __noinline static int timer_cb(void *map, int *key, struct bpf_timer *timer)
timer_test_1_ret = bpf_kfunc_multi_st_ops_test_1_assoc(&args);
recur--;
+ /* Try again later until the map is freed */
+ if (wait_map_free && timer_test_1_ret != -1) {
+ bpf_timer_start(timer, timer_ns, 0);
+ return 0;
+ }
+
timer_cb_run++;
return 0;
@@ -53,6 +66,9 @@ int BPF_PROG(test_1, struct st_ops_args *args)
bpf_timer_init(timer, &array_map, 1);
bpf_timer_set_callback(timer, timer_cb);
bpf_timer_start(timer, timer_ns, 0);
+ } else if (spin_loops) {
+ /* Stay in the trampoline while the map may be freed */
+ bpf_loop(spin_loops, spin, NULL, 0);
}
return MAP_MAGIC;
diff --git a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
index 176a355e3062..f9c4496d9fe1 100644
--- a/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
+++ b/tools/testing/selftests/bpf/progs/test_send_signal_kern.c
@@ -9,6 +9,7 @@ void bpf_task_release(struct task_struct *p) __ksym;
int bpf_send_signal_task(struct task_struct *task, int sig, enum pid_type type, u64 value) __ksym;
__u32 sig = 0, pid = 0, status = 0, signal_thread = 0, target_pid = 0;
+__u32 kworker_test = 0;
static __always_inline int bpf_send_signal_test(void *ctx)
{
@@ -60,6 +61,34 @@ int send_signal_tp_sched(void *ctx)
return bpf_send_signal_test(ctx);
}
+/*
+ * Send a signal to a task other than current, from a context where current
+ * is a kernel thread. No filtering on current is possible here, so this is
+ * driven by target_pid, and gated on kworker_test so that it stays inactive
+ * in the other tests.
+ */
+SEC("tp_btf/workqueue_execute_start")
+int send_signal_kworker(void *ctx)
+{
+ struct task_struct *target_task;
+ int ret;
+
+ if (status != 0 || !kworker_test || target_pid == 0)
+ return 0;
+
+ target_task = bpf_task_from_pid(target_pid);
+ if (!target_task)
+ return 0;
+
+ ret = bpf_send_signal_task(target_task, sig, PIDTYPE_TGID, 8);
+ bpf_task_release(target_task);
+
+ if (ret == 0)
+ status = 1;
+
+ return 0;
+}
+
SEC("perf_event")
int send_signal_perf(void *ctx)
{
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena.c b/tools/testing/selftests/bpf/progs/verifier_arena.c
index 1ce9c4b78481..b17a6dfc100d 100644
--- a/tools/testing/selftests/bpf/progs/verifier_arena.c
+++ b/tools/testing/selftests/bpf/progs/verifier_arena.c
@@ -611,6 +611,21 @@ int mixed_arena_scalar_alu64_arena_first(void *ctx)
}
SEC("syscall")
+__description("arena: a number is not an address in arena")
+__failure __msg("R6 invalid mem access 'scalar'")
+__naked void access_through_number(void)
+{
+ asm volatile (" \
+ r1 = %[arena] ll; \
+ r6 = 0x100000000000 ll; \
+ r0 = *(u64 *)(r6 + 0); \
+ exit; \
+" :
+ : __imm_addr(arena)
+ : __clobber_all);
+}
+
+SEC("syscall")
__success __retval(0)
int scalar_xor_arena_ptr(void *ctx)
{
diff --git a/tools/testing/selftests/bpf/progs/verifier_arena_scalar.c b/tools/testing/selftests/bpf/progs/verifier_arena_scalar.c
deleted file mode 100644
index 6f0efd9202ca..000000000000
--- a/tools/testing/selftests/bpf/progs/verifier_arena_scalar.c
+++ /dev/null
@@ -1,1155 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-
-#include <linux/bpf.h>
-#include <bpf/bpf_helpers.h>
-#include "../../../include/linux/filter.h"
-#include "bpf_misc.h"
-
-void *bpf_arena_alloc_pages(void *map, void *addr, __u32 page_cnt, int node_id,
- __u64 flags) __ksym;
-void bpf_arena_free_pages(void *map, void *ptr, __u32 page_cnt) __ksym;
-
-#ifdef __TARGET_ARCH_arm64
-#define ARENA_VM_START (1ull << 32)
-#else
-#define ARENA_VM_START (1ull << 44)
-#endif
-
-struct {
- __uint(type, BPF_MAP_TYPE_ARENA);
- __uint(map_flags, BPF_F_MMAPABLE);
- __uint(max_entries, 4);
- __ulong(map_extra, ARENA_VM_START);
-} arena SEC(".maps");
-
-struct {
- __uint(type, BPF_MAP_TYPE_HASH);
- __uint(max_entries, 1);
- __type(key, int);
- __type(value, long long);
-} hash SEC(".maps");
-
-/* JITs that take BPF_F_ARENA_SCALAR */
-#define __arena_scalar __flag(BPF_F_ARENA_SCALAR) __arch_x86_64 __arch_arm64
-
-/* BTF FUNC records are not generated for kfuncs referenced from inline assembly */
-void __kfunc_btf_root(void)
-{
- bpf_arena_alloc_pages(0, 0, 0, 0, 0);
- bpf_arena_free_pages(0, 0, 0);
-}
-
-/* Tests start with r6 = address of a new page as the user space sees it, a number */
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: load and store of every size")
-__success __retval(0)
-__load_if_JITed()
-__naked void ld_st_sizes(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r7 = r6; \
- r1 = 0x1122334455667788 ll; \
- *(u64 *)(r6 + 0) = r1; \
- *(u32 *)(r6 + 8) = r1; \
- *(u16 *)(r6 + 12) = r1; \
- *(u8 *)(r6 + 14) = r1; \
- *(u64 *)(r6 + 16) = 0x1234; \
- *(u32 *)(r6 + 24) = 0x5678; \
- *(u16 *)(r6 + 28) = 0x9a; \
- *(u8 *)(r6 + 30) = 0xbc; \
- r0 = 1; \
- r2 = *(u64 *)(r6 + 0); \
- if r2 != r1 goto 9f; \
- r0 = 2; \
- r2 = *(u32 *)(r6 + 8); \
- if r2 != 0x55667788 goto 9f; \
- r0 = 3; \
- r2 = *(u16 *)(r6 + 12); \
- if r2 != 0x7788 goto 9f; \
- r0 = 4; \
- r2 = *(u8 *)(r6 + 14); \
- if r2 != 0x88 goto 9f; \
- r0 = 5; \
- r2 = *(u64 *)(r6 + 16); \
- if r2 != 0x1234 goto 9f; \
- r0 = 6; \
- r2 = *(u32 *)(r6 + 24); \
- if r2 != 0x5678 goto 9f; \
- r0 = 7; \
- r2 = *(u16 *)(r6 + 28); \
- if r2 != 0x9a goto 9f; \
- r0 = 8; \
- r2 = *(u8 *)(r6 + 30); \
- if r2 != 0xbc goto 9f; \
- /* the address is what it was */ \
- r0 = 9; \
- if r6 != r7 goto 9f; \
- r0 = 10; \
- r7 >>= 32; \
- if r7 == 0 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: load into the register that holds the address")
-__success __retval(0)
-__load_if_JITed()
-__naked void ld_into_base(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r1 = 77; \
- *(u64 *)(r6 + 0) = r1; \
- r6 = *(u64 *)(r6 + 0); \
- r0 = 1; \
- if r6 != 77 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: sign extending load")
-__success __retval(0)
-__load_if_JITed()
-__naked void ldsx(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r7 = r6; \
- *(u64 *)(r6 + 0) = 0x80; \
- .8byte %[ldsx_insn]; /* r2 = *(s8 *)(r6 + 0) */ \
- r0 = 1; \
- if r2 != -128 goto 9f; \
- r0 = 2; \
- if r6 != r7 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(ldsx_insn, BPF_RAW_INSN(BPF_LDX | BPF_MEMSX | BPF_B,
- BPF_REG_2, BPF_REG_6, 0, 0))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: address loaded from arena")
-__success __retval(0)
-__load_if_JITed()
-__naked void ptr_chase(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- /* page[0] = &page[64]; page[64] = 5; */ \
- r1 = r6; \
- r1 += 64; \
- *(u64 *)(r6 + 0) = r1; \
- *(u64 *)(r1 + 0) = 5; \
- r2 = *(u64 *)(r6 + 0); \
- r0 = 1; \
- if r2 != r1 goto 9f; \
- r3 = *(u64 *)(r2 + 0); \
- r0 = 2; \
- if r3 != 5 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: atomics")
-__success __retval(0)
-__load_if_JITed()
-__naked void atomics(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r7 = r6; \
- *(u64 *)(r6 + 8) = 1; \
- r1 = 2; \
- lock *(u64 *)(r6 + 8) += r1; \
- r1 = 4; \
- .8byte %[fetch_add_insn]; /* r1 = atomic_fetch_add((u64 *)(r6 + 8), r1) */ \
- r0 = 1; \
- if r1 != 3 goto 9f; \
- r1 = 8; \
- .8byte %[xchg_insn]; /* r1 = xchg_64(r6 + 8, r1) */ \
- r0 = 2; \
- if r1 != 7 goto 9f; \
- r0 = 8; \
- r1 = 16; \
- .8byte %[cmpxchg_insn]; /* r0 = cmpxchg_64(r6 + 8, r0, r1) */ \
- r2 = r0; \
- r0 = 3; \
- if r2 != 8 goto 9f; \
- r2 = *(u64 *)(r6 + 8); \
- r0 = 4; \
- if r2 != 16 goto 9f; \
- r0 = 5; \
- if r6 != r7 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(fetch_add_insn, BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 8)),
- __imm_insn(xchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_6, BPF_REG_1, 8)),
- __imm_insn(cmpxchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_6, BPF_REG_1, 8))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: fetching and/or/xor")
-__success __retval(0)
-__load_if_JITed()
-__naked void fetch_and_or_xor(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r7 = 0x11000000110 ll; \
- *(u64 *)(r6 + 8) = r7; \
- r0 = r6; \
- r0 ^= 0x55; \
- r1 = 0x1100000011 ll; \
- .8byte %[fetch_or_dw]; /* r1 = atomic_fetch_or((u64 *)(r6 + 8), r1) */ \
- r2 = r6; \
- r2 ^= 0x55; \
- if r0 == r2 goto 1f; \
- r0 = 99; \
- goto 9f; \
-1: r0 = 1; \
- if r1 != r7 goto 9f; \
- r7 = 0x11100000111 ll; \
- r1 = 0x11000000111 ll; \
- .8byte %[fetch_and_dw]; /* r1 = atomic_fetch_and((u64 *)(r6 + 8), r1) */ \
- r0 = 2; \
- if r1 != r7 goto 9f; \
- r7 = 0x11000000111 ll; \
- r1 = 0x101 ll; \
- .8byte %[fetch_xor_dw]; /* r1 = atomic_fetch_xor((u64 *)(r6 + 8), r1) */ \
- r0 = 3; \
- if r1 != r7 goto 9f; \
- r7 = 0x11000000010 ll; \
- r2 = *(u64 *)(r6 + 8); \
- r0 = 4; \
- if r2 != r7 goto 9f; \
- *(u32 *)(r6 + 16) = 0x110; \
- r1 = 0xdead000000000011 ll; \
- .8byte %[fetch_or_w]; /* r1 = atomic_fetch_or((u32 *)(r6 + 16), r1) */ \
- r0 = 5; \
- if r1 != 0x110 goto 9f; \
- r1 = 0xdead000000000101 ll; \
- .8byte %[fetch_and_w]; /* r1 = atomic_fetch_and((u32 *)(r6 + 16), r1) */ \
- r0 = 6; \
- if r1 != 0x111 goto 9f; \
- r1 = 0xdead000000000011 ll; \
- .8byte %[fetch_xor_w]; /* r1 = atomic_fetch_xor((u32 *)(r6 + 16), r1) */ \
- r0 = 7; \
- if r1 != 0x101 goto 9f; \
- r2 = *(u32 *)(r6 + 16); \
- r0 = 8; \
- if r2 != 0x110 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(fetch_or_dw, BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 8)),
- __imm_insn(fetch_and_dw, BPF_ATOMIC_OP(BPF_DW, BPF_AND | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 8)),
- __imm_insn(fetch_xor_dw, BPF_ATOMIC_OP(BPF_DW, BPF_XOR | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 8)),
- __imm_insn(fetch_or_w, BPF_ATOMIC_OP(BPF_W, BPF_OR | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 16)),
- __imm_insn(fetch_and_w, BPF_ATOMIC_OP(BPF_W, BPF_AND | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 16)),
- __imm_insn(fetch_xor_w, BPF_ATOMIC_OP(BPF_W, BPF_XOR | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 16))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: fetching and/or/xor with the operand in r0")
-__success __retval(0)
-__load_if_JITed()
-__naked void fetch_and_or_xor_r0(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r7 = 0x11000000110 ll; \
- *(u64 *)(r6 + 8) = r7; \
- r0 = 0x1100000011 ll; \
- .8byte %[fetch_or_dw]; /* r0 = atomic_fetch_or((u64 *)(r6 + 8), r0) */ \
- r2 = r0; \
- r0 = 1; \
- if r2 != r7 goto 9f; \
- r7 = 0x11100000111 ll; \
- r0 = 0x11000000111 ll; \
- .8byte %[fetch_and_dw]; /* r0 = atomic_fetch_and((u64 *)(r6 + 8), r0) */ \
- r2 = r0; \
- r0 = 2; \
- if r2 != r7 goto 9f; \
- r7 = 0x11000000111 ll; \
- r0 = 0x101 ll; \
- .8byte %[fetch_xor_dw]; /* r0 = atomic_fetch_xor((u64 *)(r6 + 8), r0) */ \
- r2 = r0; \
- r0 = 3; \
- if r2 != r7 goto 9f; \
- r7 = 0x11000000010 ll; \
- r2 = *(u64 *)(r6 + 8); \
- r0 = 4; \
- if r2 != r7 goto 9f; \
- *(u32 *)(r6 + 16) = 0x110; \
- r0 = 0xdead000000000011 ll; \
- .8byte %[fetch_or_w]; /* r0 = atomic_fetch_or((u32 *)(r6 + 16), r0) */ \
- r2 = r0; \
- r0 = 5; \
- if r2 != 0x110 goto 9f; \
- r0 = 0xdead000000000101 ll; \
- .8byte %[fetch_and_w]; /* r0 = atomic_fetch_and((u32 *)(r6 + 16), r0) */ \
- r2 = r0; \
- r0 = 6; \
- if r2 != 0x111 goto 9f; \
- r0 = 0xdead000000000011 ll; \
- .8byte %[fetch_xor_w]; /* r0 = atomic_fetch_xor((u32 *)(r6 + 16), r0) */ \
- r2 = r0; \
- r0 = 7; \
- if r2 != 0x101 goto 9f; \
- r2 = *(u32 *)(r6 + 16); \
- r0 = 8; \
- if r2 != 0x110 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(fetch_or_dw, BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 8)),
- __imm_insn(fetch_and_dw, BPF_ATOMIC_OP(BPF_DW, BPF_AND | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 8)),
- __imm_insn(fetch_xor_dw, BPF_ATOMIC_OP(BPF_DW, BPF_XOR | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 8)),
- __imm_insn(fetch_or_w, BPF_ATOMIC_OP(BPF_W, BPF_OR | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 16)),
- __imm_insn(fetch_and_w, BPF_ATOMIC_OP(BPF_W, BPF_AND | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 16)),
- __imm_insn(fetch_xor_w, BPF_ATOMIC_OP(BPF_W, BPF_XOR | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 16))
- : __clobber_all);
-}
-
-/*
- * The page is gone, so the access faults. The fetched register has to come back
- * 0 and everything else has to be left alone, r0 included. Reading the address
- * again has to give 0 as well: had the fault been recovered by the arena mapping
- * its zeroed scratch page instead of by the JIT's exception table entry, the
- * access would have succeeded and left a 1 there.
- */
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: fetching or through a number to a freed page")
-__success __retval(0)
-__load_if_JITed()
-__naked void fetch_or_freed_page(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- *(u64 *)(r6 + 8) = 0x55; \
- r1 = %[arena] ll; \
- r2 = r6; \
- r3 = 1; \
- call %[bpf_arena_free_pages]; \
- r0 = r6; \
- r0 ^= 0x55; \
- r1 = 1; \
- .8byte %[fetch_or_dw]; /* faults: r1 = atomic_fetch_or((u64 *)(r6 + 8), r1) */ \
- r2 = r6; \
- r2 ^= 0x55; \
- if r0 == r2 goto 1f; \
- r0 = 1; \
- goto 9f; \
-1: r0 = 2; \
- if r1 != 0 goto 9f; \
- r3 = *(u64 *)(r6 + 8); \
- r0 = 3; \
- if r3 != 0 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm(bpf_arena_free_pages),
- __imm_insn(fetch_or_dw, BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH,
- BPF_REG_6, BPF_REG_1, 8))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: fetching or with the operand in r0 to a freed page")
-__success __retval(0)
-__load_if_JITed()
-__naked void fetch_or_r0_freed_page(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- *(u64 *)(r6 + 8) = 0x55; \
- r1 = %[arena] ll; \
- r2 = r6; \
- r3 = 1; \
- call %[bpf_arena_free_pages]; \
- r0 = 1; \
- .8byte %[fetch_or_dw]; /* faults: r0 = atomic_fetch_or((u64 *)(r6 + 8), r0) */ \
- r2 = r0; \
- r0 = 1; \
- if r2 != 0 goto 9f; \
- r3 = *(u64 *)(r6 + 8); \
- r0 = 2; \
- if r3 != 0 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm(bpf_arena_free_pages),
- __imm_insn(fetch_or_dw, BPF_ATOMIC_OP(BPF_DW, BPF_OR | BPF_FETCH,
- BPF_REG_6, BPF_REG_0, 8))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: cmpxchg through r0")
-__success __retval(0)
-__load_if_JITed()
-__naked void cmpxchg_r0(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- /* The address is in r0. The value at the address is not equal to it. */ \
- *(u64 *)(r6 + 0) = 3; \
- r0 = r6; \
- r1 = 5; \
- .8byte %[cmpxchg_insn]; /* r0 = cmpxchg_64(r0 + 0, r0, r1) */ \
- r2 = r0; \
- r0 = 1; \
- if r2 != 3 goto 9f; \
- r2 = *(u64 *)(r6 + 0); \
- r0 = 2; \
- if r2 != 3 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(cmpxchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_0, BPF_REG_1, 0))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: xchg into the register that holds the address")
-__success __retval(0)
-__load_if_JITed()
-__naked void xchg_into_base(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- *(u64 *)(r6 + 0) = 3; \
- r1 = r6; \
- .8byte %[xchg_insn]; /* r1 = xchg_64(r1 + 0, r1) */ \
- r0 = 1; \
- if r1 != 3 goto 9f; \
- r2 = *(u64 *)(r6 + 0); \
- r0 = 2; \
- if r2 != r6 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(xchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_1, 0))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: xchg into the register that holds a pointer to stack")
-__failure __msg("misaligned access off (0x0; 0xffffffffffffffff)+0 size 8")
-__naked void xchg_into_stack_ptr(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r1 = 0; \
- *(u64 *)(r10 - 8) = r1; \
- r1 = r10; \
- r1 += -8; \
- .8byte %[xchg_insn]; /* r1 = xchg_64(r1 + 0, r1) */ \
- r0 = 0; \
- exit; \
-" :
- : __imm_addr(arena),
- __imm_insn(xchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_1, BPF_REG_1, 0))
- : __clobber_all);
-}
-
-#ifdef CAN_USE_LOAD_ACQ_STORE_REL
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: load-acquire and store-release")
-__success __retval(0)
-__load_if_JITed()
-__naked void load_acq_store_rel(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r7 = r6; \
- r1 = 0x1234; \
- .8byte %[store_release_insn]; /* store_release((u64 *)(r6 + 8), r1) */ \
- .8byte %[load_acquire_insn]; /* r2 = load_acquire((u64 *)(r6 + 8)) */ \
- r0 = 1; \
- if r2 != 0x1234 goto 9f; \
- .8byte %[load_acquire8_insn]; /* w2 = load_acquire((u8 *)(r6 + 8)) */ \
- r0 = 2; \
- if r2 != 0x34 goto 9f; \
- r0 = 3; \
- if r6 != r7 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(store_release_insn,
- BPF_ATOMIC_OP(BPF_DW, BPF_STORE_REL, BPF_REG_6, BPF_REG_1, 8)),
- __imm_insn(load_acquire_insn,
- BPF_ATOMIC_OP(BPF_DW, BPF_LOAD_ACQ, BPF_REG_2, BPF_REG_6, 8)),
- __imm_insn(load_acquire8_insn,
- BPF_ATOMIC_OP(BPF_B, BPF_LOAD_ACQ, BPF_REG_2, BPF_REG_6, 8))
- : __clobber_all);
-}
-
-#endif /* CAN_USE_LOAD_ACQ_STORE_REL */
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: number that is not an address in arena")
-__success __retval(0)
-__load_if_JITed()
-__naked void not_in_arena(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- /* nothing is allocated: all loads read 0, stores are dropped */ \
- r6 = 0xdeadbeef00000000 ll; \
- r0 = 1; \
- r2 = *(u64 *)(r6 + 0); \
- if r2 != 0 goto 9f; \
- r0 = 2; \
- r2 = *(u8 *)(r6 - 32768); \
- if r2 != 0 goto 9f; \
- r6 = 0x12345678ffffffff ll; \
- r0 = 3; \
- r2 = *(u64 *)(r6 + 32760); \
- if r2 != 0 goto 9f; \
- *(u64 *)(r6 + 32760) = 1; \
- *(u8 *)(r6 + 32767) = r2; \
- r1 = 1; \
- lock *(u64 *)(r6 + 32760) += r1; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: store through the address of the stack as a number")
-__success __retval(0)
-__load_if_JITed()
-__naked void stack_addr_as_number(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- *(u64 *)(r10 - 8) = 5; \
- r6 = r10; \
- r6 |= 0; \
- /* r6 is a number now. The store goes to arena, not to the stack. */ \
- *(u64 *)(r6 - 8) = 7; \
- r1 = 9; \
- *(u64 *)(r6 - 8) = r1; \
- lock *(u64 *)(r6 - 8) += r1; \
- r2 = *(u64 *)(r10 - 8); \
- r0 = 1; \
- if r2 != 5 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: 64-bit math on the address stays 64-bit")
-__success __retval(0)
-__load_if_JITed()
-__naked void alu64_after_access(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r2 = *(u64 *)(r6 + 0); \
- r7 = r6; \
- r7 += 8; \
- r7 -= r6; \
- r0 = 1; \
- if r7 != 8 goto 9f; \
- r7 = r6; \
- r7 >>= 32; \
- r0 = 2; \
- if r7 == 0 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: st of an immediate changes no register")
-__success __retval(0)
-__load_if_JITed()
-__naked void st_keeps_regs(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r0 = r6; \
- r1 = 0x1111; \
- r2 = 0x2222; \
- *(u64 *)(r0 + 0) = 5; \
- r3 = r0; \
- r0 = 1; \
- if r3 != r6 goto 9f; \
- r0 = 2; \
- if r1 != 0x1111 goto 9f; \
- r0 = 3; \
- if r2 != 0x2222 goto 9f; \
- r0 = 0x3333; \
- r1 = r6; \
- *(u32 *)(r1 + 8) = -7; \
- r3 = r0; \
- r0 = 4; \
- if r3 != 0x3333 goto 9f; \
- r0 = 5; \
- if r1 != r6 goto 9f; \
- r0 = 6; \
- r3 = *(u64 *)(r6 + 0); \
- if r3 != 5 goto 9f; \
- r0 = 7; \
- r3 = *(u64 *)(r6 + 8); \
- r4 = 0xfffffff9 ll; \
- if r3 != r4 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: access through a number with garbage in the upper half")
-__success __retval(0)
-__load_if_JITed()
-__naked void st_value_garbage(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r1 = 0xdeadbeef00001000 ll; \
- *(u64 *)(r6 + 2048) = r1; \
- r7 = *(u64 *)(r6 + 2048); \
- r8 = *(u64 *)(r6 + 2048); \
- r1 = 3; \
- *(u64 *)(r8 + 0) = 1; \
- r0 = 1; \
- if r8 != r7 goto 9f; \
- *(u8 *)(r8 + 1) = 1; \
- r0 = 2; \
- if r8 != r7 goto 9f; \
- *(u32 *)(r8 + 4) = r1; \
- r0 = 3; \
- if r8 != r7 goto 9f; \
- r2 = *(u16 *)(r8 + 2); \
- r0 = 4; \
- if r8 != r7 goto 9f; \
- r0 = 5; \
- if r1 != 3 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: access through a number without the upper half")
-__success __retval(0)
-__load_if_JITed()
-__naked void st_value_small(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r1 = 0x1000 ll; \
- *(u64 *)(r6 + 2048) = r1; \
- r7 = *(u64 *)(r6 + 2048); \
- r8 = *(u64 *)(r6 + 2048); \
- r1 = 3; \
- *(u64 *)(r8 + 0) = 1; \
- r0 = 1; \
- if r8 != r7 goto 9f; \
- *(u8 *)(r8 + 1) = 1; \
- r0 = 2; \
- if r8 != r7 goto 9f; \
- *(u32 *)(r8 + 4) = r1; \
- r0 = 3; \
- if r8 != r7 goto 9f; \
- r2 = *(u16 *)(r8 + 2); \
- r0 = 4; \
- if r8 != r7 goto 9f; \
- r0 = 5; \
- if r1 != 3 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: atomics through a number that is not an address in arena")
-__success __retval(0)
-__load_if_JITed()
-__naked void atomic_value(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r1 = 0xdeadbeef00001000 ll; \
- *(u64 *)(r6 + 2048) = r1; \
- r7 = *(u64 *)(r6 + 2048); \
- r8 = *(u64 *)(r6 + 2048); \
- r1 = 3; \
- lock *(u64 *)(r8 + 0) += r1; \
- r0 = 1; \
- if r8 != r7 goto 9f; \
- .8byte %[fetch_add_insn]; \
- r0 = 2; \
- if r8 != r7 goto 9f; \
- .8byte %[xchg_insn]; \
- r0 = 3; \
- if r8 != r7 goto 9f; \
- r0 = 0; \
- .8byte %[cmpxchg_insn]; \
- r0 = 4; \
- if r8 != r7 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(fetch_add_insn, BPF_ATOMIC_OP(BPF_DW, BPF_ADD | BPF_FETCH,
- BPF_REG_8, BPF_REG_1, 0)),
- __imm_insn(xchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_XCHG, BPF_REG_8, BPF_REG_1, 0)),
- __imm_insn(cmpxchg_insn, BPF_ATOMIC_OP(BPF_DW, BPF_CMPXCHG, BPF_REG_8, BPF_REG_1, 0))
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: number and pointer to arena at the same insn")
-__success __retval(0)
-__load_if_JITed()
-__naked void mixed_number_arena(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- *(u64 *)(r6 + 8) = 0x1234; \
- /* a number that the verifier does not know, 0 at run time */ \
- r7 = *(u64 *)(r6 + 16); \
- r8 = r6; \
- if r7 != 0 goto 1f; \
- .8byte %[cast_kern_insn]; \
-1: *(u8 *)(r8 + 0) = 1; \
- *(u8 *)(r8 + 1) = r7; \
- r2 = *(u8 *)(r8 + 1); \
- lock *(u64 *)(r8 + 24) += r7; \
- r0 = 0; \
- if r7 != 0 goto 9f; \
- /* pointer to arena only: JIT adds all 64 bits of r8 to the base */ \
- r0 = 2; \
- r2 = *(u64 *)(r8 + 8); \
- if r2 != 0x1234 goto 9f; \
- r0 = 3; \
- r2 = *(u8 *)(r6 + 0); \
- if r2 != 1 goto 9f; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm_insn(cast_kern_insn, BPF_RAW_INSN(BPF_ALU64 | BPF_MOV | BPF_X,
- BPF_REG_8, BPF_REG_8, 1, 1))
- : __clobber_all);
-}
-
-SEC("syscall")
-__description("arena_scalar: no flag, no access through a number")
-__failure __msg("R6 invalid mem access 'scalar'")
-__naked void no_flag(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r6 = 0x100000000000 ll; \
- r0 = *(u64 *)(r6 + 0); \
- exit; \
-" :
- : __imm_addr(arena)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: pointer that is NULL is an address in arena")
-__success __retval(0)
-__load_if_JITed()
-__naked void null_ptr(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r1 = 0; \
- *(u32 *)(r10 - 4) = r1; \
- r2 = r10; \
- r2 += -4; \
- r1 = %[hash] ll; \
- call %[bpf_map_lookup_elem]; \
- r1 = r0; \
- r0 = 1; \
- if r1 != 0 goto 9f; \
- /* nothing is allocated: the load reads 0 */ \
- r0 = *(u64 *)(r1 + 0); \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm_addr(hash),
- __imm(bpf_map_lookup_elem)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: pointer that is NULL with an offset is an address in arena")
-__success __retval(0)
-__load_if_JITed()
-__naked void null_ptr_off(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r1 = 0; \
- *(u32 *)(r10 - 4) = r1; \
- r2 = r10; \
- r2 += -4; \
- r1 = %[hash] ll; \
- call %[bpf_map_lookup_elem]; \
- r1 = r0; \
- r0 = 1; \
- if r1 != 0 goto 9f; \
- r1 += 8; \
- *(u64 *)(r1 + 0) = 5; \
- r0 = *(u64 *)(r1 + 0); \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm_addr(hash),
- __imm(bpf_map_lookup_elem)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: number that is less than a page is an address in arena")
-__success __retval(0)
-__load_if_JITed()
-__naked void small_number(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- call %[bpf_get_prandom_u32]; \
- r1 = r0; \
- r1 &= 0xfff; \
- r0 = *(u64 *)(r1 + 0); \
- exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_get_prandom_u32)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: number is not a pointer for a helper")
-__failure __msg("R2 type=scalar expected=")
-__naked void helper_arg(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- r1 = %[hash] ll; \
- r2 = r6; \
- call %[bpf_map_lookup_elem]; \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm_addr(hash),
- __imm(bpf_arena_alloc_pages),
- __imm(bpf_map_lookup_elem)
- : __clobber_all);
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: number and pointer to stack at the same insn")
-__failure __msg("same insn cannot be used with different pointers")
-__load_if_JITed()
-__naked void mixed_number_stack(void)
-{
- asm volatile (" \
- r1 = %[arena] ll; \
- r2 = 0; \
- r3 = 1; \
- r4 = -1; \
- r5 = 0; \
- call %[bpf_arena_alloc_pages]; \
- r6 = r0; \
- r0 = 100; \
- if r6 == 0 goto 9f; \
- *(u64 *)(r10 - 8) = 0; \
- call %[bpf_get_prandom_u32]; \
- if w0 != 0 goto 1f; \
- r6 = r10; \
- r6 += -8; \
-1: r0 = *(u64 *)(r6 + 0); \
- r0 = 0; \
-9: exit; \
-" :
- : __imm_addr(arena),
- __imm(bpf_arena_alloc_pages),
- __imm(bpf_get_prandom_u32)
- : __clobber_all);
-}
-
-static int st_cb(__u64 idx, void *ctx)
-{
- volatile long *p = *(volatile long **)ctx;
-
- p[idx] = 7;
- return 0;
-}
-
-SEC("syscall")
-__arena_scalar
-__description("arena_scalar: store through a number in a callback")
-__success __retval(0)
-__load_if_JITed()
-int st_in_callback(void *unused)
-{
- volatile long *p = bpf_arena_alloc_pages(&arena, NULL, 1, -1, 0);
-
- if (!p)
- return 100;
- bpf_loop(4, st_cb, &p, 0);
- return p[0] + p[3] + p[4] - 14;
-}
-
-char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_bounds.c b/tools/testing/selftests/bpf/progs/verifier_bounds.c
index df8d5309657e..05efa9f4a522 100644
--- a/tools/testing/selftests/bpf/progs/verifier_bounds.c
+++ b/tools/testing/selftests/bpf/progs/verifier_bounds.c
@@ -2160,6 +2160,47 @@ l0_%=: r0 = 0; \
}
/*
+ * The insn that forks is the head of a loop that scalar evolution counted. The state with
+ * [-1, 0] is kept there, and the state with 0 that is split off is within it.
+ */
+SEC("socket")
+__description("maybe_fork_scalars: AND at the head of a counted loop rejects OOB")
+__failure __msg("invalid access to map value")
+__naked void and_scalar_fork_at_long_loop_head_rejects_oob(void)
+{
+ asm volatile (" \
+ r1 = 0; \
+ *(u64*)(r10 - 8) = r1; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map_hash_8b] ll; \
+ call %[bpf_map_lookup_elem]; \
+ if r0 == 0 goto l0_%=; \
+ r9 = r0; \
+ r7 = 0; \
+ r6 = -1; \
+l1_%=: r6 &= 8; \
+ /* r6 is 8 (current) or 0 (pushed) */ \
+ r6 ^= 8; \
+ /* pushed path: r6 = 8, OOB for value_size=8 */ \
+ r1 = r9; \
+ r1 += r6; \
+ r0 = *(u8*)(r1 + 0); \
+ r1 = *(u64*)(r9 + 0); \
+ r6 = -1; \
+ if r1 == 0 goto l2_%=; \
+ r6 = 0; \
+l2_%=: r7 += 1; \
+ if r7 < 100 goto l1_%=; \
+l0_%=: r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem),
+ __imm_addr(map_hash_8b)
+ : __clobber_all);
+}
+
+/*
* Last jump can be detected as always taken because the intersection of R5 and
* R7 32bit tnums produces a constant that isn't within R7's s32 bounds.
*/
diff --git a/tools/testing/selftests/bpf/progs/verifier_scev.c b/tools/testing/selftests/bpf/progs/verifier_scev.c
index 661ce9ca8f93..fa01a616af9d 100644
--- a/tools/testing/selftests/bpf/progs/verifier_scev.c
+++ b/tools/testing/selftests/bpf/progs/verifier_scev.c
@@ -1377,6 +1377,40 @@ __naked __noinline __used static int probe_read_stack(void)
: __clobber_all);
}
+/*
+ * r3 = r8 + 4 is linked to r8 when it enters the loop. Widening drops the link. If
+ * it leaves the 4 behind then r4 = r3 + 1 is taken for r3 - 3 when r3 is compared:
+ * r4 <= 7 instead of r4 <= 11, and the read of r9 looks like dead code.
+ */
+SEC("socket")
+__failure __msg("R9 !read_ok")
+__naked void widened_reg_is_not_linked(void)
+{
+ asm volatile (
+ "call %[bpf_get_prandom_u32];"
+ "r8 = r0;"
+ "r8 &= 0xff;"
+ "r3 = r8;"
+ "r3 += 4;"
+ "r1 = 0;"
+"1:"
+ "r4 = r3;"
+ "r4 += 1;"
+ "if r3 > 10 goto 3f;"
+ "if r4 <= 7 goto 2f;"
+ "r0 = *(u64 *)(r9 + 0);"
+"2:"
+ "r3 += 1;"
+ "r1 += 1;"
+ "if r1 < 2 goto 1b;"
+"3:"
+ "r0 = r8;"
+ "exit;"
+ :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
/* The unknown SCEV of an unbound STACK_MISC slot must not prevent widening. */
SEC("tracepoint")
__success
@@ -1592,7 +1626,7 @@ __msg_next(" r0=(+ r0 -1) / (linear (+ r0 -1) -1)")
__msg("loop header at 1, header_count is 3 ")
__msg("loop header at 1, widening r0 to 1..3 step 1")
__msg("1: R0=scalar(smin=umin=smin32=umin32=1,smax=umax=smax32=umax32=3,var_off=(0x0; 0x3)) loop_stack=1")
-__msg("loop header at 1, clamping r0 to 1..2 (s32 1..2) step 1")
+__msg("loop header at 1, clamping r0 to 1..3 step 1")
__msg("processed 5 insns")
__naked void post_cond_jne_neg_step(void)
{
@@ -1604,6 +1638,61 @@ __naked void post_cond_jne_neg_step(void)
" ::: __clobber_all);
}
+/*
+ * r1 and r3 are widened independently, so the r3 == 5 path reaches the back
+ * edge with r3 past the last value it takes at the header.
+ */
+SEC("socket")
+__success
+__log_level(2)
+__flag(BPF_F_TEST_STATE_FREQ)
+__msg("loop header at 2, header_count is 5 ")
+__msg("loop header at 2, widening r1 to 0..4 step 1")
+__msg("loop header at 2, widening r3 to 0..4 step 1")
+__msg("3: (55) if r3 != 0x5 goto pc+1 {{.*}}; R3=5")
+__msg("loop header at 2, clamping r3 to 0..4 step 1")
+__msg("from 6 to 2: safe")
+__naked void clamp_dependent_overshoot(void)
+{
+ asm volatile (" \
+ r1 = 0; \
+ r3 = 0; \
+1: r3 += 1; \
+ if r3 != 5 goto 2f; \
+ r2 = 0; \
+2: r1 += 1; \
+ if r1 != 5 goto 1b; \
+ r0 = 0; \
+ exit; \
+" ::: __clobber_all);
+}
+
+/* Same with a 32-bit dependent register, SCEV (zext32 (linear r3 1)) */
+SEC("socket")
+__success
+__log_level(2)
+__flag(BPF_F_TEST_STATE_FREQ)
+__msg("loop header at 2, header_count is 5 ")
+__msg("loop header at 2, widening r1 to 0..4 step 1")
+__msg("loop header at 2, widening r3 to 0..4 step 1")
+__msg("3: (56) if w3 != 0x5 goto pc+1 {{.*}}; R3=5")
+__msg("loop header at 2, clamping r3 to 0..4 step 1")
+__msg("from 6 to 2: safe")
+__naked void clamp_dependent_overshoot32(void)
+{
+ asm volatile (" \
+ r1 = 0; \
+ w3 = 0; \
+1: w3 += 1; \
+ if w3 != 5 goto 2f; \
+ r2 = 0; \
+2: r1 += 1; \
+ if r1 != 5 goto 1b; \
+ r0 = 0; \
+ exit; \
+" ::: __clobber_all);
+}
+
SEC("xdp")
__success
__log_level(2)
@@ -1771,7 +1860,7 @@ __success
__log_level(2)
__flag(BPF_F_TEST_STATE_FREQ)
__msg("loop header at 1, header_count is 4")
-__msg("loop header at 1, widening r0 to 0..3 (s32 0..3) step 1")
+__msg("loop header at 1, widening r0 to 0..3 step 1")
__msg("loop header at 1, clamping r0")
__msg("4: R0=3")
__naked void pre_cond_je32(void)
@@ -2512,6 +2601,104 @@ __naked void no_widen_any_stack_origin(void)
" ::: __clobber_all);
}
+__naked __noinline __used static int scrub_slot_callee(void)
+{
+ asm volatile (" \
+ *(u32 *)(r1 + 4) = 1; \
+ call %[bpf_get_prandom_u32]; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
+/*
+ * The callee turns the caller's fp-8 into misc, so the states at the call site
+ * differ between the first and the second iteration, while the states inside
+ * the callee are identical. Verification fails with an infinite loop error if
+ * states.c:current_loop() does not search upper frames for active loops.
+ */
+SEC("socket")
+__success
+__log_level(2)
+__flag(BPF_F_TEST_STATE_FREQ)
+__msg("loop header at 3, widening r6 to 0..9 step 1")
+__msg("loop header at 3, clamping r6 to 0..9 step 1")
+__msg("12: safe")
+__naked void clamp_callee_state_pruning(void)
+{
+ asm volatile (" \
+ w1 = 1; \
+ *(u32 *)(r10 - 8) = r1; \
+ r6 = 0; \
+1: r1 = r10; \
+ r1 += -8; \
+ call scrub_slot_callee; \
+ r6 += 1; \
+ if r6 < 10 goto 1b; \
+ r0 = *(u32 *)(r10 - 8); \
+ exit; \
+" ::: __clobber_all);
+}
+
+/*
+ * The inner loop turns fp-8 from a spill into misc. On the next outer
+ * iteration, the states inside the inner loop match its first invocation.
+ */
+SEC("socket")
+__success
+__log_level(2)
+__flag(BPF_F_TEST_STATE_FREQ)
+__msg("loop header at 3, widening r6 to 0..9 step 1")
+__msg("loop header at 4, widening r7 to 0..2 step 1")
+__msg("loop header at 3, clamping r6 to 0..9 step 1")
+__naked void clamp_nested_loop_state_pruning(void)
+{
+ asm volatile (" \
+ w1 = 1; \
+ *(u32 *)(r10 - 8) = r1; \
+ r6 = 0; \
+1: r7 = 0; \
+2: *(u32 *)(r10 - 4) = 1; \
+ r7 += 1; \
+ if r7 < 3 goto 2b; \
+ r6 += 1; \
+ if r6 < 10 goto 1b; \
+ r0 = *(u32 *)(r10 - 8); \
+ exit; \
+" ::: __clobber_all);
+}
+
+/*
+ * Same as above, but the inner loop is not recognized by SCEV and is unrolled
+ * with a precise r7. The states at its header still match between the outer
+ * iterations and must not be reported as an infinite loop: the path between
+ * them crosses the back edge of the terminating outer loop.
+ */
+SEC("socket")
+__success
+__log_level(2)
+__flag(BPF_F_TEST_STATE_FREQ)
+__msg("loop header at 3, widening r6 to 0..9 step 1")
+__msg("loop header at 5, can't compute iterations count")
+__msg("loop header at 3, clamping r6 to 0..9 step 1")
+__naked void clamp_nested_unrolled_loop_state_pruning(void)
+{
+ asm volatile (" \
+ w1 = 1; \
+ *(u32 *)(r10 - 8) = r1; \
+ r6 = 0; \
+1: *(u32 *)(r10 - 4) = 1; \
+ r7 = 1; \
+2: r7 *= 2; \
+ if r7 < 4 goto 2b; \
+ r6 += 1; \
+ if r6 < 10 goto 1b; \
+ r0 = *(u32 *)(r10 - 8); \
+ exit; \
+" ::: __clobber_all);
+}
+
/*
* r7 starts as 1 + 6*k and invariant r8 as 1 + 9*k (0 <= k <= 3).
* The (any r7 r8) union must preserve base 1 and step gcd(6, 9) = 3.
@@ -2971,6 +3158,115 @@ __naked void ptr_stack_other_frame(void)
" ::: __clobber_all);
}
+SEC("xdp")
+__success
+__log_level(2)
+__msg("scev: incompatible latch operands r6 (map_value), r7 (map_value)")
+__msg("loop header at 11, can't compute iterations count at 14")
+__naked void ptr_map_value_jmp32(void)
+{
+ asm volatile (" \
+ *(u64 *)(r10 - 8) = 0; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map] ll; \
+ call %[bpf_map_lookup_elem]; \
+ if r0 == 0 goto 2f; \
+ r6 = r0; \
+ r7 = r6; \
+ r7 += 4; \
+ r9 = 0; \
+1:" LIMIT_ITERATIONS \
+" r6 += 1; \
+ if w6 < w7 goto 1b; \
+2: r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem), __imm_addr(map)
+ : __clobber_all);
+}
+
+SEC("xdp")
+__failure
+__log_level(2)
+__msg("loop header at 11, can't compute iterations count")
+__msg("R6 32-bit pointer arithmetic prohibited")
+__naked void ptr_map_value_alu32_latch(void)
+{
+ asm volatile (" \
+ *(u64 *)(r10 - 8) = 0; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map] ll; \
+ call %[bpf_map_lookup_elem]; \
+ if r0 == 0 goto 2f; \
+ r6 = r0; \
+ r7 = r6; \
+ r7 += 4; \
+ r9 = 0; \
+1:" LIMIT_ITERATIONS \
+" w6 += 1; \
+ if r6 < r7 goto 1b; \
+2: r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem), __imm_addr(map)
+ : __clobber_all);
+}
+
+SEC("xdp")
+__success
+__log_level(2)
+__msg("loop header at 11, can't compute iterations count")
+__naked void ptr_map_value_alu32_latch2(void)
+{
+ asm volatile (" \
+ *(u64 *)(r10 - 8) = 0; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map] ll; \
+ call %[bpf_map_lookup_elem]; \
+ if r0 == 0 goto 2f; \
+ r6 = r0; \
+ r7 = r6; \
+ r7 += 4; \
+ r9 = 0; \
+1:" LIMIT_ITERATIONS \
+" r6 += 1; \
+ w8 = w6; \
+ if r8 < r7 goto 1b; \
+2: r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem), __imm_addr(map)
+ : __clobber_all);
+}
+
+SEC("xdp")
+__success
+__log_level(2)
+__msg("loop header at 8, can't widen r0, expr is (zext32 (+ r0 1))")
+__naked void ptr_map_value_alu32_non_latch(void)
+{
+ asm volatile (" \
+ *(u64 *)(r10 - 8) = 0; \
+ r2 = r10; \
+ r2 += -8; \
+ r1 = %[map] ll; \
+ call %[bpf_map_lookup_elem]; \
+ if r0 == 0 goto 2f; \
+ r1 = 0; \
+1: r1 += 1; \
+ w0 = w0; \
+ w0 += 1; \
+ if r1 < 4 goto 1b; \
+2: r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_map_lookup_elem), __imm_addr(map)
+ : __clobber_all);
+}
+
/* Metadata and packet data have different origins. */
SEC("xdp")
__success
@@ -3221,21 +3517,23 @@ __naked void ptr_mem_other_reservation(void)
: __clobber_all);
}
+/* An access to a buffer at a variable offset is rejected: the pointer must not be widened. */
SEC("iter/bpf_map_elem")
__success
__log_level(2)
__flag(BPF_F_TEST_STATE_FREQ)
__msg("header_count is 4 ")
-__msg("widening r6 to 0..3 step 1")
+__msg("can't widen r6, expr is (+ r6 8)")
__naked void ptr_buf(void)
{
asm volatile (" \
r6 = *(u64 *)(r1 + %[value]); \
if r6 == 0 goto 2f; \
- r7 = r6; \
- r7 += 4; \
-1: r6 += 1; \
- if r6 < r7 goto 1b; \
+ r7 = 0; \
+1: r0 = *(u32 *)(r6 + 0); \
+ r6 += 8; \
+ r7 += 1; \
+ if r7 < 4 goto 1b; \
2: r0 = 0; \
exit; \
" :
@@ -3351,7 +3649,7 @@ SEC("xdp")
__failure
__log_level(2)
__flag(BPF_F_TEST_STATE_FREQ)
-__msg("infinite loop detected at insn 6")
+__msg("The sequence of 8193 jumps is too complex.")
__naked void nested_terminating_prune(void)
{
asm volatile (" \
@@ -3448,4 +3746,194 @@ __naked void dominating_exit_bounds_count(void)
" ::: __clobber_all);
}
+/*
+ * The counter is copied to a callee-saved register for the call and the copy is compared.
+ */
+SEC("xdp")
+__success
+__log_level(2)
+__flag(BPF_F_TEST_STATE_FREQ)
+__msg(" scev at latch 5:")
+__msg(" r7=(zext32 r1) / (zext32 (linear r1 2))")
+__msg("loop header at 1, header_count is 100 ")
+__msg("loop header at 1, widening r1 to 0..198 step 2")
+__naked void post_cond_jlt32_copy(void)
+{
+ asm volatile (" \
+ w1 = 0; \
+1: w7 = w1; \
+ call %[bpf_get_prandom_u32]; \
+ w1 = w7; \
+ w1 += 2; \
+ if w7 < 198 goto 1b; \
+ r0 = 0; \
+ exit; \
+" :
+ : __imm(bpf_get_prandom_u32)
+ : __clobber_all);
+}
+
+/*
+ * r6 is a pointer to ringbuf memory with a step of 1. bpf_ringbuf_discard() in the first iteration
+ * makes a number of it. The offsets 0..1 of the pointer are not the range of this number: it's an
+ * address. The flag in the 32-bit spill is not widened, so the second iteration is verified
+ * on its own.
+ */
+SEC("socket")
+__failure __msg("math between map_value pointer and register with unbounded min value")
+__naked void clamp_released_pointer(void)
+{
+ asm volatile (
+ "r1 = 0;"
+ "*(u32 *)(r10 - 12) = r1;"
+ "r2 = r10;"
+ "r2 += -12;"
+ "r1 = %[small_map] ll;"
+ "call %[bpf_map_lookup_elem];"
+ "if r0 == 0 goto 9f;"
+ "r8 = r0;"
+ "r1 = %[ringbuf] ll;"
+ "r2 = 16;"
+ "r3 = 0;"
+ "call %[bpf_ringbuf_reserve];"
+ "if r0 == 0 goto 9f;"
+ "r6 = r0;"
+ "r7 = r0;"
+ "r9 = 0;"
+ "w1 = 0;"
+ "*(u32 *)(r10 - 8) = r1;"
+"1:"
+ "w4 = *(u32 *)(r10 - 8);"
+ "if w4 == 0 goto 2f;"
+ "r1 = r8;"
+ "r1 += r6;"
+ "r0 = *(u8 *)(r1 + 0);"
+ "goto 3f;"
+"2:"
+ "r1 = r7;"
+ "r2 = 0;"
+ "call %[bpf_ringbuf_discard];"
+"3:"
+ "w4 = 1;"
+ "*(u32 *)(r10 - 8) = r4;"
+ "r6 += 1;"
+ "r9 += 1;"
+ "if r9 < 2 goto 1b;"
+"9:"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(bpf_map_lookup_elem), __imm(bpf_ringbuf_reserve), __imm(bpf_ringbuf_discard),
+ __imm_addr(small_map), __imm_addr(ringbuf)
+ : __clobber_all);
+}
+
+/* The same with a packet pointer and bpf_skb_pull_data() */
+SEC("tc")
+__failure __msg("math between map_value pointer and register with unbounded min value")
+__naked void clamp_invalidated_pkt_pointer(void)
+{
+ asm volatile (
+ "r7 = r1;"
+ "r1 = 0;"
+ "*(u32 *)(r10 - 12) = r1;"
+ "r2 = r10;"
+ "r2 += -12;"
+ "r1 = %[small_map] ll;"
+ "call %[bpf_map_lookup_elem];"
+ "if r0 == 0 goto 9f;"
+ "r8 = r0;"
+ "r6 = *(u32 *)(r7 + %[data]);"
+ "r9 = 0;"
+ "w1 = 0;"
+ "*(u32 *)(r10 - 8) = r1;"
+"1:"
+ "w4 = *(u32 *)(r10 - 8);"
+ "if w4 == 0 goto 2f;"
+ "r1 = r8;"
+ "r1 += r6;"
+ "r0 = *(u8 *)(r1 + 0);"
+ "goto 3f;"
+"2:"
+ "r1 = r7;"
+ "r2 = 0;"
+ "call %[bpf_skb_pull_data];"
+"3:"
+ "w4 = 1;"
+ "*(u32 *)(r10 - 8) = r4;"
+ "r6 += 1;"
+ "r9 += 1;"
+ "if r9 < 2 goto 1b;"
+"9:"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm(bpf_map_lookup_elem), __imm(bpf_skb_pull_data), __imm_addr(small_map),
+ __imm_const(data, offsetof(struct __sk_buff, data))
+ : __clobber_all);
+}
+
+/*
+ * The compare with data_end is for r6 of this iteration: 100 bytes from r6 are in the packet. Then
+ * r6 moves by 100. The range that the compare found is not for the r6 that returns to the head.
+ */
+SEC("tc")
+__failure __msg("invalid access to packet")
+__naked void clamp_pkt_pointer_keeps_no_range(void)
+{
+ asm volatile (
+ "r7 = *(u32 *)(r1 + %[data_end]);"
+ "r6 = *(u32 *)(r1 + %[data]);"
+ "r9 = 0;"
+ "w1 = 0;"
+ "*(u32 *)(r10 - 8) = r1;"
+"1:"
+ "w4 = *(u32 *)(r10 - 8);"
+ "if w4 == 0 goto 2f;"
+ /* second iteration: nothing compared this r6 with data_end */
+ "r0 = *(u64 *)(r6 + 56);"
+"2:"
+ "r5 = r6;"
+ "r5 += 100;"
+ "if r5 > r7 goto 9f;"
+ "w4 = 1;"
+ "*(u32 *)(r10 - 8) = r4;"
+ "r6 += 100;"
+ "r9 += 1;"
+ "if r9 < 2 goto 1b;"
+"9:"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_const(data, offsetof(struct __sk_buff, data)),
+ __imm_const(data_end, offsetof(struct __sk_buff, data_end))
+ : __clobber_all);
+}
+
+/* A compare in front of the loop is for all iterations: the range at the entry stays */
+SEC("tc")
+__success __retval(0)
+__naked void clamp_pkt_pointer_keeps_range_of_entry(void)
+{
+ asm volatile (
+ "r7 = *(u32 *)(r1 + %[data_end]);"
+ "r6 = *(u32 *)(r1 + %[data]);"
+ "r5 = r6;"
+ "r5 += 40;"
+ "if r5 > r7 goto 9f;"
+ "r9 = 0;"
+"1:"
+ "r0 = *(u64 *)(r6 + 0);"
+ "r6 += 8;"
+ "r9 += 1;"
+ "if r9 < 4 goto 1b;"
+"9:"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_const(data, offsetof(struct __sk_buff, data)),
+ __imm_const(data_end, offsetof(struct __sk_buff, data_end))
+ : __clobber_all);
+}
+
char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
index b9f799f6e405..15a12497e074 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg.c
@@ -433,6 +433,125 @@ __naked void stack_arg_sequential_calls(void)
);
}
+/*
+ * A JIT passes the first stack arguments in registers. may_goto is not a call: they must be
+ * the same after it, also when it looks at the clock. That is once in 65535 iterations.
+ */
+__naked __noinline __used
+static long loops_then_reads_8args(long a, long b, long c, long d, long e, long f, long g, long h)
+{
+ asm volatile (
+ "r1 = 0;"
+ "1:"
+ "may_goto 2f;"
+ "r1 += 1;"
+ "if r1 < 70000 goto 1b;"
+ "2:"
+ "r0 = *(u64 *)(r11 + 8);"
+ "r1 = *(u64 *)(r11 + 16);"
+ "r0 += r1;"
+ "r1 = *(u64 *)(r11 + 24);"
+ "r0 += r1;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__description("stack_arg: may_goto looks at the clock, then the callee reads its args")
+__success __retval(876)
+__naked void stack_arg_may_goto_in_callee(void)
+{
+ asm volatile (
+ "r1 = 1;"
+ "r2 = 2;"
+ "r3 = 3;"
+ "r4 = 4;"
+ "r5 = 5;"
+ "*(u64 *)(r11 - 24) = 800;"
+ "*(u64 *)(r11 - 16) = 70;"
+ "*(u64 *)(r11 - 8) = 6;"
+ "call loops_then_reads_8args;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
+__naked __noinline __used
+static long spins_then_reads_8args(long a, long b, long c, long d, long e, long f, long g, long h)
+{
+ asm volatile (
+ "1:"
+ "may_goto 2f;"
+ "goto 1b;"
+ "2:"
+ "r0 = *(u64 *)(r11 + 8);"
+ "r1 = *(u64 *)(r11 + 16);"
+ "r0 += r1;"
+ "r1 = *(u64 *)(r11 + 24);"
+ "r0 += r1;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__description("stack_arg: the time of may_goto is up, then the callee reads its args")
+__success __retval(876)
+__naked void stack_arg_may_goto_timeout_in_callee(void)
+{
+ asm volatile (
+ "r1 = 1;"
+ "r2 = 2;"
+ "r3 = 3;"
+ "r4 = 4;"
+ "r5 = 5;"
+ "*(u64 *)(r11 - 24) = 800;"
+ "*(u64 *)(r11 - 16) = 70;"
+ "*(u64 *)(r11 - 8) = 6;"
+ "call spins_then_reads_8args;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
+__naked __noinline __used
+static long reads_8args(long a, long b, long c, long d, long e, long f, long g, long h)
+{
+ asm volatile (
+ "r0 = *(u64 *)(r11 + 8);"
+ "r1 = *(u64 *)(r11 + 16);"
+ "r0 += r1;"
+ "r1 = *(u64 *)(r11 + 24);"
+ "r0 += r1;"
+ "exit;"
+ );
+}
+
+SEC("tc")
+__description("stack_arg: may_goto looks at the clock between the stores of the args and the call")
+__success __retval(876)
+__naked void stack_arg_may_goto_in_caller(void)
+{
+ asm volatile (
+ "*(u64 *)(r11 - 24) = 800;"
+ "*(u64 *)(r11 - 16) = 70;"
+ "*(u64 *)(r11 - 8) = 6;"
+ "r6 = 0;"
+ "1:"
+ "may_goto 2f;"
+ "r6 += 1;"
+ "if r6 < 70000 goto 1b;"
+ "2:"
+ "r1 = 1;"
+ "r2 = 2;"
+ "r3 = 3;"
+ "r4 = 4;"
+ "r5 = 5;"
+ "call reads_8args;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
#else
SEC("socket")
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c b/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c
index ab1955852233..5bfcd300c7a0 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c
@@ -5,6 +5,17 @@
#include <bpf/bpf_helpers.h>
#include "bpf_misc.h"
+struct timer_value {
+ struct bpf_timer timer;
+};
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARRAY);
+ __uint(max_entries, 1);
+ __type(key, int);
+ __type(value, struct timer_value);
+} timer_map SEC(".maps");
+
#if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64) || \
(defined(__TARGET_ARCH_riscv) && __riscv_xlen == 64)) && \
defined(__BPF_FEATURE_STACK_ARGUMENT)
@@ -130,6 +141,25 @@ static void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, in
);
}
+__noinline __used __naked
+static int callback_6args(long a, long b, long c, long d, long e, long f)
+{
+ asm volatile ("r0 = *(u64 *)(r11 + 8);"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
+__noinline __used __naked
+static void subprog_call_bad_ptr_7args(void)
+{
+ asm volatile ("*(u64 *)(r11 - 8) = 0;"
+ "call subprog_bad_ptr_7args;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
SEC("tc")
__description("stack_arg: read without caller write")
__failure
@@ -152,7 +182,7 @@ __naked void stack_arg_read_without_write_1(void)
SEC("tc")
__description("stack_arg: read with not-initialized caller write")
__failure
-__msg("R0 !read_ok")
+__msg("invalid read from uninitialized stack arg off 8")
__btf_func_path("btf__verifier_stack_arg_order.bpf.o")
__naked void stack_arg_read_without_write_2(void)
{
@@ -171,6 +201,95 @@ __naked void stack_arg_read_without_write_2(void)
);
}
+SEC("tc")
+__description("stack_arg: unreliable callee with too few stack args")
+__failure
+__msg("invalid read from uninitialized stack arg off 16")
+__btf_func_path("btf__verifier_stack_arg_order.bpf.o")
+__naked void stack_arg_read_with_short_bridge_write(void)
+{
+ asm volatile ("r1 = 0;"
+ "r2 = 0;"
+ "r3 = 0;"
+ "r4 = 0;"
+ "r5 = 0;"
+ "*(u64 *)(r11 - 8) = 0;"
+ "*(u64 *)(r11 - 16) = 0;"
+ "call subprog_bad_ptr_7args;"
+ "call subprog_call_bad_ptr_7args;"
+ "exit;"
+ ::: __clobber_all
+ );
+}
+
+SEC("tc")
+__description("stack_arg: synchronous callback with stack args")
+__failure
+__msg("callback subprog cannot have stack args")
+__btf_func_path("btf__verifier_stack_arg_order.bpf.o")
+__naked void stack_arg_sync_callback(void)
+{
+ asm volatile ("r6 = 0;"
+ "*(u64 *)(r11 - 8) = r6;"
+ "r1 = 1;"
+ "r2 = %[callback_6args];"
+ "r3 = 0;"
+ "r4 = 0;"
+ "call %[bpf_loop];"
+ "r1 = 1;"
+ "r2 = 2;"
+ "r3 = 3;"
+ "r4 = 4;"
+ "r5 = 5;"
+ "*(u64 *)(r11 - 8) = 6;"
+ "call callback_6args;"
+ "r0 = 0;"
+ "exit;"
+ :
+ : __imm_ptr(callback_6args),
+ __imm(bpf_loop)
+ : __clobber_common, "r6"
+ );
+}
+
+SEC("tc")
+__description("stack_arg: asynchronous callback with stack args")
+__failure
+__msg("callback subprog cannot have stack args")
+__btf_func_path("btf__verifier_stack_arg_order.bpf.o")
+__naked void stack_arg_async_callback(void)
+{
+ asm volatile ("r1 = 0;"
+ "*(u32 *)(r10 - 4) = r1;"
+ "r1 = %[timer_map] ll;"
+ "r2 = r10;"
+ "r2 += -4;"
+ "call %[bpf_map_lookup_elem];"
+ "if r0 == 0 goto l0_%=;"
+ "r6 = r0;"
+ "r7 = 0;"
+ "*(u64 *)(r11 - 8) = r7;"
+ "r1 = r6;"
+ "r2 = %[callback_6args];"
+ "call %[bpf_timer_set_callback];"
+ "r1 = 1;"
+ "r2 = 2;"
+ "r3 = 3;"
+ "r4 = 4;"
+ "r5 = 5;"
+ "*(u64 *)(r11 - 8) = 6;"
+ "call callback_6args;"
+ "l0_%=: r0 = 0;"
+ "exit;"
+ :
+ : __imm_addr(timer_map),
+ __imm_ptr(callback_6args),
+ __imm(bpf_map_lookup_elem),
+ __imm(bpf_timer_set_callback)
+ : __clobber_common, "r6", "r7"
+ );
+}
+
#else
SEC("socket")
diff --git a/tools/testing/selftests/bpf/progs/verifier_stack_ptr.c b/tools/testing/selftests/bpf/progs/verifier_stack_ptr.c
index 7fa7bc207850..e6a9021b197b 100644
--- a/tools/testing/selftests/bpf/progs/verifier_stack_ptr.c
+++ b/tools/testing/selftests/bpf/progs/verifier_stack_ptr.c
@@ -588,35 +588,37 @@ __naked void stack_check_size_512_with_may_goto(void)
/*
* Verify that old PTR_TO_STACK state is considered a super-set of
- * new PTR_TO_STACK state when new variable range is a sub-range
- * of the old range, e.g. old [-72, -16] vs new [-64, -16].
+ * new PTR_TO_STACK state when the new offset is within the old range,
+ * e.g. old [-72, -16] vs new {-16}.
*/
SEC("socket")
__success
__log_level(2)
__flag(BPF_F_TEST_STATE_FREQ)
/*
- * r7 is widened to [-72, -16] at the loop header (insn 3),
- * the loop body sees [-64, -8] after 'r7 += 8'
+ * r7 is widened to [-72, -16] at the loop header (insn 5),
+ * the loop body sees [-64, -8] after 'r7 += 8'.
*/
-__msg("loop header at 3, widening r7 to -72..-16 step 8")
+__msg("loop header at 5, widening r7 to -72..-16 step 1")
__msg("R7=fp(smin=smin32=-64,smax=smax32=-8")
-/*
- * back-edge state is clamped to the remaining iterations and pruned
- * at the header, because [-64, -16] is within the widened [-72, -16]
- */
-__msg("loop header at 3, clamping r7 to -64..-16 (s32 -64..-8) step 8")
-__msg("from 5 to 3: safe")
-__msg("processed 9 insns")
+/* 'r7 = r8' sets r7 to fp-16; its ANY SCEV is not clamped. */
+__msg("R7=fp-16 R8=fp-16 loop_stack=5")
+__not_msg("clamping r7")
+/* back-edge state is pruned because {-16} is within [-72, -16]. */
+__msg("from 8 to 5: safe")
+__msg("processed 12 insns")
__naked void stack_ptr_subrange_in_loop(void)
{
asm volatile (" \
r7 = r10; \
r7 += -72; \
+ r8 = r10; \
+ r8 += -16; \
r6 = 0; \
- r7 += 8; \
+1: r7 += 8; \
+ r7 = r8; \
r6 += 1; \
- if r6 < 8 goto -3; \
+ if r6 < 8 goto 1b; \
r0 = 0; \
exit; \
" ::: __clobber_all);
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
index 55398c04290a..25f37a3dd7cf 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_accept.c
@@ -13,6 +13,23 @@ static char buf[64];
SEC("lsm.s/file_open")
__success
+int BPF_PROG(inode_field_before_file_put)
+{
+ struct file *file;
+ ino_t ino;
+
+ file = bpf_get_task_exe_file(bpf_get_current_task_btf());
+ if (!file)
+ return 0;
+
+ ino = file->f_inode->i_ino;
+ bpf_put_file(file);
+ __sink(ino);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
+__success
int BPF_PROG(get_task_exe_file_and_put_kfunc_from_current_sleepable)
{
struct file *acquired;
diff --git a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
index b5f456d57669..ed2bd2faebf3 100644
--- a/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
+++ b/tools/testing/selftests/bpf/progs/verifier_vfs_reject.c
@@ -13,6 +13,25 @@
static char buf[PATH_MAX];
SEC("lsm.s/file_open")
+__failure __msg("invalid mem access 'scalar'")
+int BPF_PROG(inode_field_after_file_put)
+{
+ struct inode *inode;
+ struct file *file;
+ ino_t ino;
+
+ file = bpf_get_task_exe_file(bpf_get_current_task_btf());
+ if (!file)
+ return 0;
+
+ inode = file->f_inode;
+ bpf_put_file(file);
+ ino = inode->i_ino;
+ __sink(ino);
+ return 0;
+}
+
+SEC("lsm.s/file_open")
__failure __msg("Possibly NULL pointer passed to trusted R1")
int BPF_PROG(get_task_exe_file_kfunc_null)
{
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 6380f52f4dd2..76fbe64755ef 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -281,6 +281,27 @@ __bpf_kfunc void bpf_iter_testmod_seq_destroy(struct bpf_iter_testmod_seq *it)
it->cnt = 0;
}
+__bpf_kfunc int bpf_iter_testmod_current_new(struct bpf_iter_testmod_current *it)
+{
+ it->remaining = 2;
+ return 0;
+}
+
+__bpf_kfunc struct task_struct *
+bpf_iter_testmod_current_next(struct bpf_iter_testmod_current *it)
+{
+ if (!it->remaining)
+ return NULL;
+ it->remaining--;
+ /* current remains alive independently of the iterator. */
+ return current;
+}
+
+__bpf_kfunc void bpf_iter_testmod_current_destroy(struct bpf_iter_testmod_current *it)
+{
+ it->remaining = 0;
+}
+
__bpf_kfunc void bpf_kfunc_common_test(void)
{
}
@@ -1016,6 +1037,10 @@ BTF_ID_FLAGS(func, bpf_iter_testmod_seq_new, KF_ITER_NEW)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_next, KF_ITER_NEXT | KF_RET_NULL)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_iter_testmod_seq_value)
+BTF_ID_FLAGS(func, bpf_iter_testmod_current_new, KF_ITER_NEW)
+BTF_ID_FLAGS(func, bpf_iter_testmod_current_next,
+ KF_ITER_NEXT | KF_RET_NULL | KF_ITER_RET_INDEPENDENT)
+BTF_ID_FLAGS(func, bpf_iter_testmod_current_destroy, KF_ITER_DESTROY)
BTF_ID_FLAGS(func, bpf_kfunc_common_test)
BTF_ID_FLAGS(func, bpf_kfunc_arena_arg_test)
BTF_ID_FLAGS(func, bpf_kfunc_arena_cap_test)
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
index 4ba9def74404..aef780261ef4 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
@@ -31,6 +31,10 @@ struct bpf_iter_testmod_seq {
int cnt;
};
+struct bpf_iter_testmod_current {
+ u64 remaining;
+};
+
struct bpf_testmod_ops {
int (*test_1)(void);
void (*test_2)(int a, int b);
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
index 3b810f1e438f..e9902e651d72 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod_kfunc.h
@@ -33,6 +33,11 @@ struct prog_test_ref_kfunc {
#endif
struct bpf_iter_testmod_seq;
+struct bpf_iter_testmod_current;
+
+int bpf_iter_testmod_current_new(struct bpf_iter_testmod_current *it) __ksym;
+struct task_struct *bpf_iter_testmod_current_next(struct bpf_iter_testmod_current *it) __ksym;
+void bpf_iter_testmod_current_destroy(struct bpf_iter_testmod_current *it) __ksym;
struct prog_test_pass1 {
int x0;
diff --git a/tools/testing/selftests/bpf/test_loader.c b/tools/testing/selftests/bpf/test_loader.c
index 824e5912ebcb..d24e4fedae46 100644
--- a/tools/testing/selftests/bpf/test_loader.c
+++ b/tools/testing/selftests/bpf/test_loader.c
@@ -590,8 +590,6 @@ static int parse_test_spec(struct test_loader *tester,
update_flags(&spec->prog_flags, BPF_F_XDP_HAS_FRAGS, clear);
} else if (strcmp(val, "BPF_F_TEST_REG_INVARIANTS") == 0) {
update_flags(&spec->prog_flags, BPF_F_TEST_REG_INVARIANTS, clear);
- } else if (strcmp(val, "BPF_F_ARENA_SCALAR") == 0) {
- update_flags(&spec->prog_flags, BPF_F_ARENA_SCALAR, clear);
} else /* assume numeric value */ {
err = parse_int(val, &flags, "test prog flags");
if (err)