[PATCH bpf-next v1] selftests/bpf: Add test for indirect struct_ops trampoline

Tiezhu Yang <[email protected]>
Newsgroups dev.linux.lists.loongarch,org.kernel.vger.bpf,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Add a test case to verify that arguments passed on the stack are correctly
read by indirect struct_ops trampoline. This test ensures the correctness
of stack offsets on architectures like LoongArch and RISC-V.

Signed-off-by: Tiezhu Yang <[email protected]>
---
This is to test the following two patches:

bpf, riscv: Fix stack-passed arguments for indirect trampolines
https://lore.kernel.org/bpf/[email protected]/

bpf, loongarch: Fix stack arguments for indirect trampolines
https://lore.kernel.org/bpf/[email protected]/

 .../prog_tests/test_struct_ops_trampoline.c   | 41 +++++++++++++++++++
 .../bpf/progs/struct_ops_trampoline.c         | 24 +++++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.c    | 18 ++++++++
 .../selftests/bpf/test_kmods/bpf_testmod.h    |  5 +++
 4 files changed, 88 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/test_struct_ops_trampoline.c
 create mode 100644 tools/testing/selftests/bpf/progs/struct_ops_trampoline.c

diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_trampoline.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_trampoline.c
new file mode 100644
index 000000000000..5a21aedd02fc
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_trampoline.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "struct_ops_trampoline.skel.h"
+
+#if defined(__loongarch__) || defined(__riscv)
+static void run_struct_ops_trampoline(void)
+{
+	struct struct_ops_trampoline *skel;
+	struct bpf_link *link;
+	int err;
+
+	skel = struct_ops_trampoline__open();
+	if (!ASSERT_OK_PTR(skel, "struct_ops_trampoline__open"))
+		return;
+
+	err = struct_ops_trampoline__load(skel);
+	if (!ASSERT_OK(err, "struct_ops_trampoline__load"))
+		goto cleanup;
+
+	link = bpf_map__attach_struct_ops(skel->maps.testmod_trampoline);
+	if (!ASSERT_OK_PTR(link, "attach_struct_ops"))
+		goto cleanup;
+
+	ASSERT_OK(trigger_module_test_read(256), "trigger_read");
+
+	ASSERT_EQ(skel->bss->got_arg9, 9999, "check_stack_passed_arg9");
+
+	bpf_link__destroy(link);
+cleanup:
+	struct_ops_trampoline__destroy(skel);
+}
+#endif
+
+void test_struct_ops_trampoline(void)
+{
+#if defined(__loongarch__) || defined(__riscv)
+	run_struct_ops_trampoline();
+#else
+	test__skip();
+#endif
+}
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_trampoline.c b/tools/testing/selftests/bpf/progs/struct_ops_trampoline.c
new file mode 100644
index 000000000000..b0fc19a2a0c5
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/struct_ops_trampoline.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <vmlinux.h>
+#include <bpf/bpf_tracing.h>
+#include "../test_kmods/bpf_testmod.h"
+#include "bpf_misc.h"
+
+char _license[] SEC("license") = "GPL";
+
+__u64 got_arg9 = 0;
+
+SEC("struct_ops/test_trampoline")
+int BPF_PROG(test_trampoline, int arg1, int arg2, int arg3,
+			      int arg4, int arg5, int arg6,
+			      int arg7, int arg8, int arg9)
+{
+	got_arg9 = arg9;
+
+	return 0;
+}
+
+SEC(".struct_ops.link")
+struct bpf_testmod_ops testmod_trampoline = {
+	.test_trampoline = (void *)test_trampoline,
+};
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
index 850cf4f830c4..be96bbc1b7c0 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
@@ -589,6 +589,9 @@ noinline int bpf_testmod_trampoline_count_test(void)
 	return 0;
 }
 
+struct bpf_testmod_ops;
+static struct bpf_testmod_ops *st_ops_trampoline;
+
 noinline ssize_t
 bpf_testmod_test_read(struct file *file, struct kobject *kobj,
 		      const struct bin_attribute *bin_attr,
@@ -637,6 +640,9 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj,
 
 	bpf_testmod_test_struct_ops3();
 
+	if (st_ops_trampoline && st_ops_trampoline->test_trampoline)
+		st_ops_trampoline->test_trampoline(1, 2, 3, 4, 5, 6, 7, 8, 9999);
+
 	struct_arg3 = kmalloc((sizeof(struct bpf_testmod_struct_arg_3) +
 				sizeof(int)), GFP_KERNEL);
 	if (struct_arg3 != NULL) {
@@ -1619,6 +1625,10 @@ static int bpf_testmod_ops_init_member(const struct btf_type *t,
 		((struct bpf_testmod_ops *)kdata)->data = ((struct bpf_testmod_ops *)udata)->data;
 		return 1;
 	}
+
+	if (member->offset == offsetof(struct bpf_testmod_ops, test_trampoline) * 8)
+		st_ops_trampoline = (struct bpf_testmod_ops *)kdata;
+
 	return 0;
 }
 
@@ -1694,6 +1704,13 @@ bpf_testmod_ops__test_return_ref_kptr(int dummy, struct task_struct *task__ref,
 	return NULL;
 }
 
+static int bpf_testmod_ops__test_trampoline(int arg1, int arg2, int arg3,
+					    int arg4, int arg5, int arg6,
+					    int arg7, int arg8, int arg9)
+{
+	return arg9;
+}
+
 static struct bpf_testmod_ops __bpf_testmod_ops = {
 	.test_1 = bpf_testmod_test_1,
 	.test_2 = bpf_testmod_test_2,
@@ -1701,6 +1718,7 @@ static struct bpf_testmod_ops __bpf_testmod_ops = {
 	.test_refcounted = bpf_testmod_ops__test_refcounted,
 	.test_refcounted_multi = bpf_testmod_ops__test_refcounted_multi,
 	.test_return_ref_kptr = bpf_testmod_ops__test_return_ref_kptr,
+	.test_trampoline = bpf_testmod_ops__test_trampoline,
 };
 
 struct bpf_struct_ops bpf_bpf_testmod_ops = {
diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
index 210b919290cc..a5349d09ce55 100644
--- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
+++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.h
@@ -46,6 +46,11 @@ struct bpf_testmod_ops {
 	struct task_struct *(*test_return_ref_kptr)(int dummy, struct task_struct *task,
 						    struct cgroup *cgrp);
 
+	/* Used to test indirect struct_ops trampolines stack-passed arguments (>8 args). */
+	int (*test_trampoline)(int arg1, int arg2, int arg3,
+			       int arg4, int arg5, int arg6,
+			       int arg7, int arg8, int arg9);
+
 	/* The following fields are used to test shadow copies. */
 	char onebyte;
 	struct {
-- 
2.42.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.