If a 0-length struct is passed as a parameter this throws off assumptions that register N in calling convention will match parameter N. With [1], such function representations will correctly be emitted in BTF because the register/param mismatch is not a result of an optimization we cannot infer from the code alone. Test that BPF_PROG2() macro can handle this situation by having a function with a 0-length struct as first arg and verify that we see expected 2nd, 3rd arg values. [1] https://lore.kernel.org/dwarves/20250502070318.1561924-1-tony.ambardar@xxxxxxxxx/ Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> --- .../selftests/bpf/prog_tests/tracing_struct.c | 2 ++ tools/testing/selftests/bpf/progs/tracing_struct.c | 11 +++++++++++ tools/testing/selftests/bpf/test_kmods/bpf_testmod.c | 12 ++++++++++++ 3 files changed, 25 insertions(+) diff --git a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c index 19e68d4b3532..5b7e80fb8ccc 100644 --- a/tools/testing/selftests/bpf/prog_tests/tracing_struct.c +++ b/tools/testing/selftests/bpf/prog_tests/tracing_struct.c @@ -56,6 +56,8 @@ static void test_struct_args(void) ASSERT_EQ(skel->bss->t6, 1, "t6 ret"); + ASSERT_EQ(skel->bss->t7, 34, "t7 a + c"); + destroy_skel: tracing_struct__destroy(skel); } diff --git a/tools/testing/selftests/bpf/progs/tracing_struct.c b/tools/testing/selftests/bpf/progs/tracing_struct.c index c435a3a8328a..5d181be11a96 100644 --- a/tools/testing/selftests/bpf/progs/tracing_struct.c +++ b/tools/testing/selftests/bpf/progs/tracing_struct.c @@ -18,6 +18,9 @@ struct bpf_testmod_struct_arg_3 { int b[]; }; +struct bpf_testmod_struct_arg_6 { +}; + long t1_a_a, t1_a_b, t1_b, t1_c, t1_ret, t1_nregs; __u64 t1_reg0, t1_reg1, t1_reg2, t1_reg3; long t2_a, t2_b_a, t2_b_b, t2_c, t2_ret; @@ -25,6 +28,7 @@ long t3_a, t3_b, t3_c_a, t3_c_b, t3_ret; long t4_a_a, t4_b, t4_c, t4_d, t4_e_a, t4_e_b, t4_ret; long t5_ret; int t6; +int t7; SEC("fentry/bpf_testmod_test_struct_arg_1") int BPF_PROG2(test_struct_arg_1, struct bpf_testmod_struct_arg_2, a, int, b, int, c) @@ -130,4 +134,11 @@ int BPF_PROG2(test_struct_arg_11, struct bpf_testmod_struct_arg_3 *, a) return 0; } +SEC("fentry/bpf_testmod_test_struct_arg_10") +int BPF_PROG2(test_struct_arg_12, struct bpf_testmod_struct_arg_6, h, u64, a, short, c) +{ + t7 = a + c; + return 0; +} + char _license[] SEC("license") = "GPL"; diff --git a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c index 2e54b95ad898..4a766e5ee9bc 100644 --- a/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c +++ b/tools/testing/selftests/bpf/test_kmods/bpf_testmod.c @@ -62,6 +62,9 @@ struct bpf_testmod_struct_arg_5 { long d; }; +struct bpf_testmod_struct_arg_6 { +}; + __bpf_hook_start(); noinline int @@ -128,6 +131,13 @@ bpf_testmod_test_struct_arg_9(u64 a, void *b, short c, int d, void *e, char f, return bpf_testmod_test_struct_arg_result; } +noinline int +bpf_testmod_test_struct_arg_10(struct bpf_testmod_struct_arg_6 h, u64 a, short c) +{ + bpf_testmod_test_struct_arg_result = a + c; + return bpf_testmod_test_struct_arg_result; +} + noinline int bpf_testmod_test_arg_ptr_to_struct(struct bpf_testmod_struct_arg_1 *a) { bpf_testmod_test_struct_arg_result = a->a; @@ -398,6 +408,7 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj, struct bpf_testmod_struct_arg_3 *struct_arg3; struct bpf_testmod_struct_arg_4 struct_arg4 = {21, 22}; struct bpf_testmod_struct_arg_5 struct_arg5 = {23, 24, 25, 26}; + struct bpf_testmod_struct_arg_6 struct_arg6 = {}; int i = 1; while (bpf_testmod_return_ptr(i)) @@ -414,6 +425,7 @@ bpf_testmod_test_read(struct file *file, struct kobject *kobj, (void *)20, struct_arg4, 23); (void)bpf_testmod_test_struct_arg_9(16, (void *)17, 18, 19, (void *)20, 21, 22, struct_arg5, 27); + (void)bpf_testmod_test_struct_arg_10(struct_arg6, 16, 18); (void)bpf_testmod_test_arg_ptr_to_struct(&struct_arg1_2); -- 2.39.3