Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'bpf-allow-union-argument-in-trampoline-based-programs'

Leon Hwang says:

====================
bpf: Allow union argument in trampoline based programs

While tracing 'release_pages' with bpfsnoop[0], the verifier reports:

The function release_pages arg0 type UNION is unsupported.

However, it should be acceptable to trace functions that have 'union'
arguments.

This patch set enables such support in the verifier by allowing 'union'
as a valid argument type.

Changes:
v3 -> v4:
* Address comments from Alexei:
* Trim bpftrace output in patch #1 log.
* Drop the referenced commit info and the test output in patch #2 log.

v2 -> v3:
* Address comments from Alexei:
* Reuse the existing flag BTF_FMODEL_STRUCT_ARG.
* Update the comment of the flag BTF_FMODEL_STRUCT_ARG.

v1 -> v2:
* Add 16B 'union' argument support in x86_64 trampoline.
* Update selftests using bpf_testmod.
* Add test case about 16-bytes 'union' argument.
* Address comments from Alexei:
* Study the patch set about 'struct' argument support.
* Update selftests to cover more cases.
v1: https://lore.kernel.org/bpf/20250905133226.84675-1-leon.hwang@linux.dev/

Links:
[0] https://github.com/bpfsnoop/bpfsnoop
====================

Link: https://patch.msgid.link/20250919044110.23729-1-leon.hwang@linux.dev
Signed-off-by: Alexei Starovoitov <ast@kernel.org>

+98 -5
+1 -1
include/linux/bpf.h
··· 1128 1128 */ 1129 1129 #define MAX_BPF_FUNC_REG_ARGS 5 1130 1130 1131 - /* The argument is a structure. */ 1131 + /* The argument is a structure or a union. */ 1132 1132 #define BTF_FMODEL_STRUCT_ARG BIT(0) 1133 1133 1134 1134 /* The argument is signed. */
+4 -4
kernel/bpf/btf.c
··· 6751 6751 /* skip modifiers */ 6752 6752 while (btf_type_is_modifier(t)) 6753 6753 t = btf_type_by_id(btf, t->type); 6754 - if (btf_type_is_small_int(t) || btf_is_any_enum(t) || __btf_type_is_struct(t)) 6754 + if (btf_type_is_small_int(t) || btf_is_any_enum(t) || btf_type_is_struct(t)) 6755 6755 /* accessing a scalar */ 6756 6756 return true; 6757 6757 if (!btf_type_is_ptr(t)) { ··· 7323 7323 if (btf_type_is_ptr(t)) 7324 7324 /* kernel size of pointer. Not BPF's size of pointer*/ 7325 7325 return sizeof(void *); 7326 - if (btf_type_is_int(t) || btf_is_any_enum(t) || __btf_type_is_struct(t)) 7326 + if (btf_type_is_int(t) || btf_is_any_enum(t) || btf_type_is_struct(t)) 7327 7327 return t->size; 7328 7328 return -EINVAL; 7329 7329 } ··· 7332 7332 { 7333 7333 u8 flags = 0; 7334 7334 7335 - if (__btf_type_is_struct(t)) 7335 + if (btf_type_is_struct(t)) 7336 7336 flags |= BTF_FMODEL_STRUCT_ARG; 7337 7337 if (btf_type_is_signed_int(t)) 7338 7338 flags |= BTF_FMODEL_SIGNED_ARG; ··· 7373 7373 return -EINVAL; 7374 7374 } 7375 7375 ret = __get_type_size(btf, func->type, &t); 7376 - if (ret < 0 || __btf_type_is_struct(t)) { 7376 + if (ret < 0 || btf_type_is_struct(t)) { 7377 7377 bpf_log(log, 7378 7378 "The function %s return type %s is unsupported.\n", 7379 7379 tname, btf_type_str(t));
+29
tools/testing/selftests/bpf/prog_tests/tracing_struct.c
··· 112 112 tracing_struct_many_args__destroy(skel); 113 113 } 114 114 115 + static void test_union_args(void) 116 + { 117 + struct tracing_struct *skel; 118 + int err; 119 + 120 + skel = tracing_struct__open_and_load(); 121 + if (!ASSERT_OK_PTR(skel, "tracing_struct__open_and_load")) 122 + return; 123 + 124 + err = tracing_struct__attach(skel); 125 + if (!ASSERT_OK(err, "tracing_struct__attach")) 126 + goto out; 127 + 128 + ASSERT_OK(trigger_module_test_read(256), "trigger_read"); 129 + 130 + ASSERT_EQ(skel->bss->ut1_a_a, 1, "ut1:a.arg.a"); 131 + ASSERT_EQ(skel->bss->ut1_b, 4, "ut1:b"); 132 + ASSERT_EQ(skel->bss->ut1_c, 5, "ut1:c"); 133 + 134 + ASSERT_EQ(skel->bss->ut2_a, 6, "ut2:a"); 135 + ASSERT_EQ(skel->bss->ut2_b_a, 2, "ut2:b.arg.a"); 136 + ASSERT_EQ(skel->bss->ut2_b_b, 3, "ut2:b.arg.b"); 137 + 138 + out: 139 + tracing_struct__destroy(skel); 140 + } 141 + 115 142 void test_tracing_struct(void) 116 143 { 117 144 if (test__start_subtest("struct_args")) 118 145 test_struct_args(); 119 146 if (test__start_subtest("struct_many_args")) 120 147 test_struct_many_args(); 148 + if (test__start_subtest("union_args")) 149 + test_union_args(); 121 150 }
+33
tools/testing/selftests/bpf/progs/tracing_struct.c
··· 18 18 int b[]; 19 19 }; 20 20 21 + union bpf_testmod_union_arg_1 { 22 + char a; 23 + short b; 24 + struct bpf_testmod_struct_arg_1 arg; 25 + }; 26 + 27 + union bpf_testmod_union_arg_2 { 28 + int a; 29 + long b; 30 + struct bpf_testmod_struct_arg_2 arg; 31 + }; 32 + 21 33 long t1_a_a, t1_a_b, t1_b, t1_c, t1_ret, t1_nregs; 22 34 __u64 t1_reg0, t1_reg1, t1_reg2, t1_reg3; 23 35 long t2_a, t2_b_a, t2_b_b, t2_c, t2_ret; ··· 37 25 long t4_a_a, t4_b, t4_c, t4_d, t4_e_a, t4_e_b, t4_ret; 38 26 long t5_ret; 39 27 int t6; 28 + 29 + long ut1_a_a, ut1_b, ut1_c; 30 + long ut2_a, ut2_b_a, ut2_b_b; 40 31 41 32 SEC("fentry/bpf_testmod_test_struct_arg_1") 42 33 int BPF_PROG2(test_struct_arg_1, struct bpf_testmod_struct_arg_2, a, int, b, int, c) ··· 142 127 int BPF_PROG2(test_struct_arg_11, struct bpf_testmod_struct_arg_3 *, a) 143 128 { 144 129 t6 = a->b[0]; 130 + return 0; 131 + } 132 + 133 + SEC("fexit/bpf_testmod_test_union_arg_1") 134 + int BPF_PROG2(test_union_arg_1, union bpf_testmod_union_arg_1, a, int, b, int, c) 135 + { 136 + ut1_a_a = a.arg.a; 137 + ut1_b = b; 138 + ut1_c = c; 139 + return 0; 140 + } 141 + 142 + SEC("fexit/bpf_testmod_test_union_arg_2") 143 + int BPF_PROG2(test_union_arg_2, int, a, union bpf_testmod_union_arg_2, b) 144 + { 145 + ut2_a = a; 146 + ut2_b_a = b.arg.a; 147 + ut2_b_b = b.arg.b; 145 148 return 0; 146 149 } 147 150
+31
tools/testing/selftests/bpf/test_kmods/bpf_testmod.c
··· 62 62 long d; 63 63 }; 64 64 65 + union bpf_testmod_union_arg_1 { 66 + char a; 67 + short b; 68 + struct bpf_testmod_struct_arg_1 arg; 69 + }; 70 + 71 + union bpf_testmod_union_arg_2 { 72 + int a; 73 + long b; 74 + struct bpf_testmod_struct_arg_2 arg; 75 + }; 76 + 65 77 __bpf_hook_start(); 66 78 67 79 noinline int ··· 137 125 { 138 126 bpf_testmod_test_struct_arg_result = a + (long)b + c + d + (long)e + 139 127 f + g + h.a + h.b + h.c + h.d + i; 128 + return bpf_testmod_test_struct_arg_result; 129 + } 130 + 131 + noinline int 132 + bpf_testmod_test_union_arg_1(union bpf_testmod_union_arg_1 a, int b, int c) 133 + { 134 + bpf_testmod_test_struct_arg_result = a.arg.a + b + c; 135 + return bpf_testmod_test_struct_arg_result; 136 + } 137 + 138 + noinline int 139 + bpf_testmod_test_union_arg_2(int a, union bpf_testmod_union_arg_2 b) 140 + { 141 + bpf_testmod_test_struct_arg_result = a + b.arg.a + b.arg.b; 140 142 return bpf_testmod_test_struct_arg_result; 141 143 } 142 144 ··· 434 408 struct bpf_testmod_struct_arg_3 *struct_arg3; 435 409 struct bpf_testmod_struct_arg_4 struct_arg4 = {21, 22}; 436 410 struct bpf_testmod_struct_arg_5 struct_arg5 = {23, 24, 25, 26}; 411 + union bpf_testmod_union_arg_1 union_arg1 = { .arg = {1} }; 412 + union bpf_testmod_union_arg_2 union_arg2 = { .arg = {2, 3} }; 437 413 int i = 1; 438 414 439 415 while (bpf_testmod_return_ptr(i)) ··· 452 424 (void *)20, struct_arg4, 23); 453 425 (void)bpf_testmod_test_struct_arg_9(16, (void *)17, 18, 19, (void *)20, 454 426 21, 22, struct_arg5, 27); 427 + 428 + (void)bpf_testmod_test_union_arg_1(union_arg1, 4, 5); 429 + (void)bpf_testmod_test_union_arg_2(6, union_arg2); 455 430 456 431 (void)bpf_testmod_test_arg_ptr_to_struct(&struct_arg1_2); 457 432