From: Emil Tsalapatis <emil@etsalapatis.com>
To: bpf@vger.kernel.org
Cc: ast@kernel.org, andrii@kernel.org, eddyz87@gmail.com,
memxor@gmail.com, daniel@iogearbox.net,
Emil Tsalapatis <emil@etsalapatis.com>
Subject: [PATCH bpf 2/2] selftests/bpf: add arena fault recovery tests
Date: Wed, 28 Oct 2026 01:51:41 +0000 [thread overview]
Message-ID: <20261028015141.10710-3-emil@etsalapatis.com> (raw)
In-Reply-To: <20261028015141.10710-1-emil@etsalapatis.com>
Test the arena fault handling code with two new selftests.
The first one confirms that a program without an arena (and
by extension a code path that doesn't involve BPF at all)
properly recovers after accessing an unbacked arena page.
The second test deploys the same configuration for a program
that has its own arena map.
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
---
tools/testing/selftests/bpf/Makefile | 2 +-
.../selftests/bpf/prog_tests/arena_fault.c | 91 +++++++++++++++++++
.../testing/selftests/bpf/progs/arena_fault.c | 55 +++++++++++
.../selftests/bpf/progs/arena_fault_noarena.c | 17 ++++
.../testing/selftests/bpf/test_kmods/Makefile | 2 +-
.../bpf/test_kmods/bpf_test_arena_fault.c | 55 +++++++++++
.../bpf/test_kmods/bpf_test_arena_fault.h | 10 ++
7 files changed, 230 insertions(+), 2 deletions(-)
create mode 100644 tools/testing/selftests/bpf/prog_tests/arena_fault.c
create mode 100644 tools/testing/selftests/bpf/progs/arena_fault.c
create mode 100644 tools/testing/selftests/bpf/progs/arena_fault_noarena.c
create mode 100644 tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.c
create mode 100644 tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.h
diff --git a/tools/testing/selftests/bpf/Makefile b/tools/testing/selftests/bpf/Makefile
index 1e4af62e8312..97d4a266abed 100644
--- a/tools/testing/selftests/bpf/Makefile
+++ b/tools/testing/selftests/bpf/Makefile
@@ -47,7 +47,7 @@ TEST_PROGS_EXTENDED := \
ima_setup.sh verify_sig_setup.sh
TEST_KMODS := bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
- bpf_test_modorder_y.ko bpf_test_rqspinlock.ko
+ bpf_test_modorder_y.ko bpf_test_rqspinlock.ko bpf_test_arena_fault.ko
TEST_KMOD_TARGETS = $(addprefix $(OUTPUT)/,$(TEST_KMODS))
# Compile but not part of 'make run_tests'
diff --git a/tools/testing/selftests/bpf/prog_tests/arena_fault.c b/tools/testing/selftests/bpf/prog_tests/arena_fault.c
new file mode 100644
index 000000000000..7134ece16a31
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/arena_fault.c
@@ -0,0 +1,91 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <test_progs.h>
+#include <testing_helpers.h>
+#include "arena_fault.skel.h"
+#include "arena_fault_noarena.skel.h"
+
+#define ARENA_FAULT_MOD "bpf_test_arena_fault"
+
+static int run_syscall_prog(struct bpf_program *prog, const char *name)
+{
+ LIBBPF_OPTS(bpf_test_run_opts, opts);
+ int err;
+
+ err = bpf_prog_test_run_opts(bpf_program__fd(prog), &opts);
+ if (!ASSERT_OK(err, name))
+ return -1;
+ if (!ASSERT_OK(opts.retval, name))
+ return -1;
+ return 0;
+}
+
+static void test_wrong_arena(void)
+{
+ struct arena_fault *fault_skel = NULL;
+ struct arena_fault *stash_skel = NULL;
+
+ stash_skel = arena_fault__open_and_load();
+ if (!ASSERT_OK_PTR(stash_skel, "open_load_stash"))
+ return;
+
+ fault_skel = arena_fault__open_and_load();
+ if (!ASSERT_OK_PTR(fault_skel, "open_load_fault"))
+ goto out;
+
+ if (run_syscall_prog(stash_skel->progs.stash_and_free, "stash_and_free"))
+ goto out;
+ run_syscall_prog(fault_skel->progs.fault_stashed_wrong_arena,
+ "fault_stashed_wrong_arena");
+
+out:
+ if (stash_skel)
+ run_syscall_prog(stash_skel->progs.clear_stashed, "clear_stashed");
+ arena_fault__destroy(fault_skel);
+ arena_fault__destroy(stash_skel);
+}
+
+static void test_no_arena(void)
+{
+ struct arena_fault_noarena *fault_skel = NULL;
+ struct arena_fault *stash_skel = NULL;
+
+ stash_skel = arena_fault__open_and_load();
+ if (!ASSERT_OK_PTR(stash_skel, "open_load_stash"))
+ return;
+
+ fault_skel = arena_fault_noarena__open_and_load();
+ if (!ASSERT_OK_PTR(fault_skel, "open_load_noarena"))
+ goto out;
+
+ if (run_syscall_prog(stash_skel->progs.stash_and_free, "stash_and_free"))
+ goto out;
+ run_syscall_prog(fault_skel->progs.fault_stashed_noarena,
+ "fault_stashed_noarena");
+
+out:
+ if (stash_skel)
+ run_syscall_prog(stash_skel->progs.clear_stashed, "clear_stashed");
+ arena_fault_noarena__destroy(fault_skel);
+ arena_fault__destroy(stash_skel);
+}
+
+void serial_test_arena_fault(void)
+{
+#if !defined(__x86_64__) && !defined(__aarch64__)
+ test__skip();
+ return;
+#endif
+
+ if (!ASSERT_OK(load_module(ARENA_FAULT_MOD ".ko",
+ env_verbosity > VERBOSE_NONE),
+ "load " ARENA_FAULT_MOD ".ko"))
+ return;
+
+ if (test__start_subtest("wrong_arena"))
+ test_wrong_arena();
+ if (test__start_subtest("no_arena"))
+ test_no_arena();
+
+ unload_module(ARENA_FAULT_MOD, env_verbosity > VERBOSE_NONE);
+}
diff --git a/tools/testing/selftests/bpf/progs/arena_fault.c b/tools/testing/selftests/bpf/progs/arena_fault.c
new file mode 100644
index 000000000000..1967b128fdf6
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/arena_fault.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define BPF_NO_KFUNC_PROTOTYPES
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "bpf_arena_common.h"
+#include "../test_kmods/bpf_test_arena_fault.h"
+
+struct {
+ __uint(type, BPF_MAP_TYPE_ARENA);
+ __uint(map_flags, BPF_F_MMAPABLE);
+ __uint(max_entries, 2);
+} arena SEC(".maps");
+
+u64 __arena arena_pad;
+volatile u64 observed;
+
+SEC("syscall")
+int stash_and_free(void *ctx)
+{
+ u64 __arena *val;
+
+ val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+ if (!val)
+ return 1;
+
+ *val = 42;
+ bpf_test_arena_fault_stash((u64 *)val);
+ bpf_arena_free_pages(&arena, val, 1);
+ return 0;
+}
+
+SEC("syscall")
+int fault_stashed_wrong_arena(void *ctx)
+{
+ u64 __arena *val;
+
+ val = bpf_arena_alloc_pages(&arena, NULL, 1, NUMA_NO_NODE, 0);
+ if (!val)
+ return 1;
+
+ *val = 1;
+ observed = bpf_test_arena_fault_load_stashed();
+ bpf_arena_free_pages(&arena, val, 1);
+ return observed != 0;
+}
+
+SEC("syscall")
+int clear_stashed(void *ctx)
+{
+ bpf_test_arena_fault_clear_stashed();
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/arena_fault_noarena.c b/tools/testing/selftests/bpf/progs/arena_fault_noarena.c
new file mode 100644
index 000000000000..7fa8814808b5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/arena_fault_noarena.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#define BPF_NO_KFUNC_PROTOTYPES
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include "../test_kmods/bpf_test_arena_fault.h"
+
+volatile u64 observed;
+
+SEC("syscall")
+int fault_stashed_noarena(void *ctx)
+{
+ observed = bpf_test_arena_fault_load_stashed();
+ return observed != 0;
+}
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/test_kmods/Makefile b/tools/testing/selftests/bpf/test_kmods/Makefile
index 031c7454ce65..e611e71fcef1 100644
--- a/tools/testing/selftests/bpf/test_kmods/Makefile
+++ b/tools/testing/selftests/bpf/test_kmods/Makefile
@@ -19,7 +19,7 @@ Q = @
endif
MODULES = bpf_testmod.ko bpf_test_no_cfi.ko bpf_test_modorder_x.ko \
- bpf_test_modorder_y.ko bpf_test_rqspinlock.ko
+ bpf_test_modorder_y.ko bpf_test_rqspinlock.ko bpf_test_arena_fault.ko
$(foreach m,$(MODULES),$(eval obj-m += $(m:.ko=.o)))
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.c b/tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.c
new file mode 100644
index 000000000000..0b3dbf80344d
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.c
@@ -0,0 +1,55 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/bpf.h>
+#include <linux/btf.h>
+#include <linux/module.h>
+
+static u64 *stashed_ptr;
+
+__bpf_kfunc_start_defs();
+
+__bpf_kfunc void bpf_test_arena_fault_stash(u64 *ptr__arena)
+{
+ WRITE_ONCE(stashed_ptr, ptr__arena);
+}
+
+__bpf_kfunc u64 bpf_test_arena_fault_load_stashed(void)
+{
+ u64 *ptr = READ_ONCE(stashed_ptr);
+
+ return ptr ? READ_ONCE(*ptr) : U64_MAX;
+}
+
+__bpf_kfunc void bpf_test_arena_fault_clear_stashed(void)
+{
+ WRITE_ONCE(stashed_ptr, NULL);
+}
+
+__bpf_kfunc_end_defs();
+
+BTF_KFUNCS_START(bpf_test_arena_fault_kfunc_ids)
+BTF_ID_FLAGS(func, bpf_test_arena_fault_stash)
+BTF_ID_FLAGS(func, bpf_test_arena_fault_load_stashed)
+BTF_ID_FLAGS(func, bpf_test_arena_fault_clear_stashed)
+BTF_KFUNCS_END(bpf_test_arena_fault_kfunc_ids)
+
+static const struct btf_kfunc_id_set bpf_test_arena_fault_kfunc_set = {
+ .owner = THIS_MODULE,
+ .set = &bpf_test_arena_fault_kfunc_ids,
+};
+
+static int __init bpf_test_arena_fault_init(void)
+{
+ return register_btf_kfunc_id_set(BPF_PROG_TYPE_UNSPEC,
+ &bpf_test_arena_fault_kfunc_set);
+}
+
+static void __exit bpf_test_arena_fault_exit(void)
+{
+}
+
+module_init(bpf_test_arena_fault_init);
+module_exit(bpf_test_arena_fault_exit);
+
+MODULE_DESCRIPTION("BPF arena fault selftest module");
+MODULE_LICENSE("GPL");
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.h b/tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.h
new file mode 100644
index 000000000000..1657c445a0fa
--- /dev/null
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_test_arena_fault.h
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#ifndef __BPF_TEST_ARENA_FAULT_H
+#define __BPF_TEST_ARENA_FAULT_H
+
+void bpf_test_arena_fault_stash(__u64 *ptr__arena) __ksym;
+__u64 bpf_test_arena_fault_load_stashed(void) __ksym;
+void bpf_test_arena_fault_clear_stashed(void) __ksym;
+
+#endif /* __BPF_TEST_ARENA_FAULT_H */
--
2.54.0
next prev parent reply other threads:[~2026-10-28 1:51 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-28 1:51 [PATCH bpf-next 0/2] bpf: expand BPF arena kernel fault handling Emil Tsalapatis
2026-10-28 1:51 ` [PATCH bpf 1/2] bpf: recover page faults across all arenas Emil Tsalapatis
2026-10-28 2:36 ` bot+bpf-ci
2026-10-28 1:51 ` Emil Tsalapatis [this message]
2026-10-28 2:13 ` [PATCH bpf 2/2] selftests/bpf: add arena fault recovery tests sashiko-bot
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20261028015141.10710-3-emil@etsalapatis.com \
--to=emil@etsalapatis.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=memxor@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.