Add two adjacent bitfield members between two non-bitfield members. Check the size of the generated C structure and the offsets of both non-bitfield members. Require libbpf to reject the first bitfield member and print the expected message. Move a non-bitfield member to the first byte after a four-byte local struct_ops type. Require an -EINVAL result without a memory error. Assisted-by: LLM Signed-off-by: Mingpei CAO --- .../bpf/prog_tests/test_struct_ops_module.c | 91 ++++++++++++++++++- .../selftests/bpf/progs/struct_ops_module.c | 17 ++++ 2 files changed, 107 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c index 75a0dea511b3f..41029ebb22f01 100644 --- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c +++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */ #include +#include #include #include @@ -150,6 +151,91 @@ static void test_struct_ops_not_zeroed(void) struct_ops_module__destroy(skel); } +static void test_struct_ops_local_bitfield(void) +{ + struct struct_ops_module *skel; + char *log = NULL; + int err; + + skel = struct_ops_module__open(); + if (!ASSERT_OK_PTR(skel, "struct_ops_module_open_local_bitfield")) + return; + + if (!ASSERT_EQ(offsetof(typeof(*skel->struct_ops.testmod_local_bitfield), data), + 0, "local_bitfield_data_offset") || + !ASSERT_EQ(offsetof(typeof(*skel->struct_ops.testmod_local_bitfield), onebyte), + 8, "local_bitfield_onebyte_offset")) + goto cleanup; + if (!ASSERT_EQ(sizeof(*skel->struct_ops.testmod_local_bitfield), + bpf_map__value_size(skel->maps.testmod_local_bitfield), + "local_bitfield_shadow_size")) + goto cleanup; + + /* Normal fields around the unsupported bitfield remain accessible. */ + skel->struct_ops.testmod_local_bitfield->data = 1; + skel->struct_ops.testmod_local_bitfield->onebyte = 1; + + err = bpf_map__set_autocreate(skel->maps.testmod_local_bitfield, true); + if (!ASSERT_OK(err, "enable_local_bitfield_map")) + goto cleanup; + + if (start_libbpf_log_capture()) + goto cleanup; + err = struct_ops_module__load(skel); + log = stop_libbpf_log_capture(); + if (!ASSERT_EQ(err, -ENOTSUP, "struct_ops_module_load_local_bitfield")) + goto cleanup; + ASSERT_HAS_SUBSTR(log, "local bitfield extra_bitfield is not supported", + "local_bitfield_rejection_log"); + +cleanup: + free(log); + struct_ops_module__destroy(skel); +} + +static void test_struct_ops_bad_member_offset(void) +{ + struct struct_ops_module *skel; + struct btf_member *member; + struct btf_type *type; + struct btf *btf; + char *log = NULL; + int err, type_id; + + skel = struct_ops_module__open(); + if (!ASSERT_OK_PTR(skel, "struct_ops_module_open_bad_offset")) + return; + + btf = bpf_object__btf(skel->obj); + type_id = btf__find_by_name_kind(btf, "bpf_testmod_ops___bad_offset", + BTF_KIND_STRUCT); + if (!ASSERT_GT(type_id, 0, "find_bad_offset_type")) + goto cleanup; + type = (struct btf_type *)btf__type_by_id(btf, type_id); + if (!ASSERT_OK_PTR(type, "get_bad_offset_type")) + goto cleanup; + + member = btf_members(type); + member->offset = type->size * 8; + + err = bpf_map__set_autocreate(skel->maps.testmod_bad_offset, true); + if (!ASSERT_OK(err, "enable_bad_offset_map")) + goto cleanup; + + if (start_libbpf_log_capture()) + goto cleanup; + err = struct_ops_module__load(skel); + log = stop_libbpf_log_capture(); + if (!ASSERT_EQ(err, -EINVAL, "struct_ops_module_load_bad_offset")) + goto cleanup; + ASSERT_HAS_SUBSTR(log, "member extra is outside the 4-byte local struct_ops type", + "bad_offset_rejection_log"); + +cleanup: + free(log); + struct_ops_module__destroy(skel); +} + /* The signature of an implementation might not match the signature of the * function pointer prototype defined in the BPF program. This mismatch * should be allowed as long as the behavior of the operator program @@ -304,6 +390,10 @@ void serial_test_struct_ops_module(void) test_struct_ops_load(); if (test__start_subtest("struct_ops_not_zeroed")) test_struct_ops_not_zeroed(); + if (test__start_subtest("struct_ops_local_bitfield")) + test_struct_ops_local_bitfield(); + if (test__start_subtest("struct_ops_bad_member_offset")) + test_struct_ops_bad_member_offset(); if (test__start_subtest("struct_ops_incompatible")) test_struct_ops_incompatible(); if (test__start_subtest("struct_ops_null_out_cb")) @@ -314,4 +404,3 @@ void serial_test_struct_ops_module(void) test_detach_link(); RUN_TESTS(unsupported_ops); } - diff --git a/tools/testing/selftests/bpf/progs/struct_ops_module.c b/tools/testing/selftests/bpf/progs/struct_ops_module.c index 71c420c3a5a6c..846b5a934dc83 100644 --- a/tools/testing/selftests/bpf/progs/struct_ops_module.c +++ b/tools/testing/selftests/bpf/progs/struct_ops_module.c @@ -76,6 +76,23 @@ struct bpf_testmod_ops___zeroed testmod_zeroed = { .zeroed_op = (void *)zeroed_op, }; +struct bpf_testmod_ops___local_bitfield { + int data; + unsigned int extra_bitfield:31; + unsigned int extra_bitfield2:1; + char onebyte; +}; + +SEC("?.struct_ops.link") +struct bpf_testmod_ops___local_bitfield testmod_local_bitfield; + +struct bpf_testmod_ops___bad_offset { + unsigned int extra; +}; + +SEC("?.struct_ops.link") +struct bpf_testmod_ops___bad_offset testmod_bad_offset; + struct bpf_testmod_ops___incompatible { int (*test_1)(void); void (*test_2)(int *a); -- 2.43.0