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

selftests/bpf: Make sure libbpf doesn't enforce the signature of a func pointer.

The verifier in the kernel ensures that the struct_ops operators behave
correctly by checking that they access parameters and context
appropriately. The verifier will approve a program as long as it correctly
accesses the context/parameters, regardless of its function signature. In
contrast, libbpf should not verify the signature of function pointers and
functions to enable flexibility in loading various implementations of an
operator even if the signature of the function pointer does not match those
in the implementations or the kernel.

With this flexibility, user space applications can adapt to different
kernel versions by loading a specific implementation of an operator based
on feature detection.

This is a follow-up of the commit c911fc61a7ce ("libbpf: Skip zeroed or
null fields if not found in the kernel type.")

Signed-off-by: Kui-Feng Lee <thinker.li@gmail.com>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20240404232342.991414-1-thinker.li@gmail.com

authored by

Kui-Feng Lee and committed by
Andrii Nakryiko
ba0cbe2b 27095479

+37
+24
tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c
··· 138 138 struct_ops_module__destroy(skel); 139 139 } 140 140 141 + /* The signature of an implementation might not match the signature of the 142 + * function pointer prototype defined in the BPF program. This mismatch 143 + * should be allowed as long as the behavior of the operator program 144 + * adheres to the signature in the kernel. Libbpf should not enforce the 145 + * signature; rather, let the kernel verifier handle the enforcement. 146 + */ 147 + static void test_struct_ops_incompatible(void) 148 + { 149 + struct struct_ops_module *skel; 150 + struct bpf_link *link; 151 + 152 + skel = struct_ops_module__open_and_load(); 153 + if (!ASSERT_OK_PTR(skel, "open_and_load")) 154 + return; 155 + 156 + link = bpf_map__attach_struct_ops(skel->maps.testmod_incompatible); 157 + if (ASSERT_OK_PTR(link, "attach_struct_ops")) 158 + bpf_link__destroy(link); 159 + 160 + struct_ops_module__destroy(skel); 161 + } 162 + 141 163 void serial_test_struct_ops_module(void) 142 164 { 143 165 if (test__start_subtest("test_struct_ops_load")) 144 166 test_struct_ops_load(); 145 167 if (test__start_subtest("test_struct_ops_not_zeroed")) 146 168 test_struct_ops_not_zeroed(); 169 + if (test__start_subtest("test_struct_ops_incompatible")) 170 + test_struct_ops_incompatible(); 147 171 } 148 172
+13
tools/testing/selftests/bpf/progs/struct_ops_module.c
··· 68 68 .test_1 = (void *)test_1, 69 69 .test_2 = (void *)test_2_v2, 70 70 }; 71 + 72 + struct bpf_testmod_ops___incompatible { 73 + int (*test_1)(void); 74 + void (*test_2)(int *a); 75 + int data; 76 + }; 77 + 78 + SEC(".struct_ops.link") 79 + struct bpf_testmod_ops___incompatible testmod_incompatible = { 80 + .test_1 = (void *)test_1, 81 + .test_2 = (void *)test_2, 82 + .data = 3, 83 + };