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 --- This is to test the following two patches: bpf, riscv: Fix stack-passed arguments for indirect trampolines https://lore.kernel.org/bpf/20260821233516.3426127-3-memxor@gmail.com/ bpf, loongarch: Fix stack arguments for indirect trampolines https://lore.kernel.org/bpf/20260821233516.3426127-8-memxor@gmail.com/ .../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 +#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 +#include +#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