* [PATCH bpf v3 1/2] libbpf: Reject local struct_ops bitfields before data access
2026-09-15 16:22 [PATCH bpf v3 0/2] libbpf: Reject local struct_ops bitfields before data access Mingpei CAO
@ 2026-09-15 16:22 ` Mingpei CAO
2026-09-15 17:40 ` Amery Hung
2026-09-15 16:22 ` [PATCH bpf v3 2/2] selftests/bpf: Test local struct_ops bitfield rejection Mingpei CAO
2026-09-17 22:30 ` [PATCH bpf v3 0/2] libbpf: Reject local struct_ops bitfields before data access patchwork-bot+netdevbpf
2 siblings, 1 reply; 6+ messages in thread
From: Mingpei CAO @ 2026-09-15 16:22 UTC (permalink / raw)
To: bpf; +Cc: Mingpei CAO, andrii, eddyz87, ameryhung
A local struct_ops type can contain a bitfield absent from the
corresponding kernel BTF type. bpf_map__init_kern_struct_ops() checks
whether data for an absent local member is zero.
For a bitfield, member->offset contains the bit offset and field width.
bpf_map__init_kern_struct_ops() used the encoded value to calculate the
member data pointer before rejecting the bitfield. The zero-data check
could therefore read outside st_ops->data and crash libbpf.
Reject local bitfields before calculating the member data pointer. Keep
the existing rejection for bitfields in the kernel type.
Fixes: c911fc61a7ce ("libbpf: Skip zeroed or null fields if not found in the kernel type.")
Assisted-by: LLM
Signed-off-by: Mingpei CAO <caomingpei@gmail.com>
---
tools/lib/bpf/libbpf.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b749c01742ee0..84ff5e3be8964 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1223,6 +1223,12 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
const char *mname;
mname = btf__name_by_offset(btf, member->name_off);
+ if (btf_member_bitfield_size(type, i)) {
+ pr_warn("struct_ops init_kern %s: local bitfield %s is not supported\n",
+ map->name, mname);
+ return -ENOTSUP;
+ }
+
moff = member->offset / 8;
mdata = data + moff;
msize = btf__resolve_size(btf, member->type);
@@ -1259,8 +1265,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
}
kern_member_idx = kern_member - btf_members(kern_type);
- if (btf_member_bitfield_size(type, i) ||
- btf_member_bitfield_size(kern_type, kern_member_idx)) {
+ if (btf_member_bitfield_size(kern_type, kern_member_idx)) {
pr_warn("struct_ops init_kern %s: bitfield %s is not supported\n",
map->name, mname);
return -ENOTSUP;
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH bpf v3 1/2] libbpf: Reject local struct_ops bitfields before data access
2026-09-15 16:22 ` [PATCH bpf v3 1/2] " Mingpei CAO
@ 2026-09-15 17:40 ` Amery Hung
0 siblings, 0 replies; 6+ messages in thread
From: Amery Hung @ 2026-09-15 17:40 UTC (permalink / raw)
To: Mingpei CAO; +Cc: bpf, andrii, eddyz87
On Tue, Sep 15, 2026 at 9:23 AM Mingpei CAO <caomingpei@gmail.com> wrote:
>
> A local struct_ops type can contain a bitfield absent from the
> corresponding kernel BTF type. bpf_map__init_kern_struct_ops() checks
> whether data for an absent local member is zero.
>
> For a bitfield, member->offset contains the bit offset and field width.
> bpf_map__init_kern_struct_ops() used the encoded value to calculate the
> member data pointer before rejecting the bitfield. The zero-data check
> could therefore read outside st_ops->data and crash libbpf.
>
> Reject local bitfields before calculating the member data pointer. Keep
> the existing rejection for bitfields in the kernel type.
>
> Fixes: c911fc61a7ce ("libbpf: Skip zeroed or null fields if not found in the kernel type.")
> Assisted-by: LLM
> Signed-off-by: Mingpei CAO <caomingpei@gmail.com>
Reviewed-by: Amery Hung <ameryhung@gmail.com>
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf v3 2/2] selftests/bpf: Test local struct_ops bitfield rejection
2026-09-15 16:22 [PATCH bpf v3 0/2] libbpf: Reject local struct_ops bitfields before data access Mingpei CAO
2026-09-15 16:22 ` [PATCH bpf v3 1/2] " Mingpei CAO
@ 2026-09-15 16:22 ` Mingpei CAO
2026-09-15 17:30 ` bot+bpf-ci
2026-09-17 22:30 ` [PATCH bpf v3 0/2] libbpf: Reject local struct_ops bitfields before data access patchwork-bot+netdevbpf
2 siblings, 1 reply; 6+ messages in thread
From: Mingpei CAO @ 2026-09-15 16:22 UTC (permalink / raw)
To: bpf; +Cc: Mingpei CAO, andrii, eddyz87, ameryhung
Add a local struct_ops type with a bitfield absent from the corresponding
kernel BTF type. Verify that libbpf returns -ENOTSUP and emits the local
bitfield diagnostic.
Keep the existing all-zero scalar test as the compatibility control.
Assisted-by: LLM
Signed-off-by: Mingpei CAO <caomingpei@gmail.com>
---
.../bpf/prog_tests/test_struct_ops_module.c | 32 +++++++++++++++++++
.../selftests/bpf/progs/struct_ops_module.c | 7 ++++
2 files changed, 39 insertions(+)
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..03f86abafeb7c 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
@@ -150,6 +150,36 @@ 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;
+
+ bpf_map__set_autocreate(skel->maps.testmod_zeroed, false);
+ 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);
+}
+
/* 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 +334,8 @@ 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_incompatible"))
test_struct_ops_incompatible();
if (test__start_subtest("struct_ops_null_out_cb"))
diff --git a/tools/testing/selftests/bpf/progs/struct_ops_module.c b/tools/testing/selftests/bpf/progs/struct_ops_module.c
index 71c420c3a5a6c..f221bfdbddabf 100644
--- a/tools/testing/selftests/bpf/progs/struct_ops_module.c
+++ b/tools/testing/selftests/bpf/progs/struct_ops_module.c
@@ -76,6 +76,13 @@ struct bpf_testmod_ops___zeroed testmod_zeroed = {
.zeroed_op = (void *)zeroed_op,
};
+struct bpf_testmod_ops___local_bitfield {
+ unsigned int extra_bitfield:31;
+};
+
+SEC("?.struct_ops.link")
+struct bpf_testmod_ops___local_bitfield testmod_local_bitfield;
+
struct bpf_testmod_ops___incompatible {
int (*test_1)(void);
void (*test_2)(int *a);
--
2.43.0
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH bpf v3 2/2] selftests/bpf: Test local struct_ops bitfield rejection
2026-09-15 16:22 ` [PATCH bpf v3 2/2] selftests/bpf: Test local struct_ops bitfield rejection Mingpei CAO
@ 2026-09-15 17:30 ` bot+bpf-ci
0 siblings, 0 replies; 6+ messages in thread
From: bot+bpf-ci @ 2026-09-15 17:30 UTC (permalink / raw)
To: caomingpei, bpf
Cc: caomingpei, andrii, eddyz87, ameryhung, ast, andrii, daniel,
martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 2364 bytes --]
> 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..03f86abafeb7c 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
> @@ -150,6 +150,36 @@ 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;
> +
> + bpf_map__set_autocreate(skel->maps.testmod_zeroed, false);
> + err = bpf_map__set_autocreate(skel->maps.testmod_local_bitfield, true);
> + if (!ASSERT_OK(err, "enable_local_bitfield_map"))
> + goto cleanup;
This isn't a bug, but since bpf_map__set_autocreate() can only fail once
the map exists, could the second call drop the err variable and ASSERT_OK
check? The other callers in this file at lines 75 and 199 call it
unchecked between __open() and __load().
[ ... ]
> @@ -304,6 +334,8 @@ 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_incompatible"))
> test_struct_ops_incompatible();
> if (test__start_subtest("struct_ops_null_out_cb"))
[ ... ]
> selftests/bpf: Test local struct_ops bitfield rejection
>
> Add a local struct_ops type with a bitfield absent from the corresponding
> kernel BTF type. Verify that libbpf returns -ENOTSUP and emits the local
> bitfield diagnostic.
>
> Keep the existing all-zero scalar test as the compatibility control.
This isn't a bug, but could the last line be clarified? The existing
zeroed test is not modified in this patch, and the message does not say
what specific behavior the new subtest guards against.
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/34996121618
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf v3 0/2] libbpf: Reject local struct_ops bitfields before data access
2026-09-15 16:22 [PATCH bpf v3 0/2] libbpf: Reject local struct_ops bitfields before data access Mingpei CAO
2026-09-15 16:22 ` [PATCH bpf v3 1/2] " Mingpei CAO
2026-09-15 16:22 ` [PATCH bpf v3 2/2] selftests/bpf: Test local struct_ops bitfield rejection Mingpei CAO
@ 2026-09-17 22:30 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-17 22:30 UTC (permalink / raw)
To: Mingpei CAO; +Cc: bpf, andrii, eddyz87, ameryhung
Hello:
This series was applied to bpf/bpf-next.git (master)
by Andrii Nakryiko <andrii@kernel.org>:
On Tue, 15 Sep 2026 16:22:35 +0000 you wrote:
> A local struct_ops type can contain a bitfield absent from the
> corresponding kernel BTF type. libbpf calculated the member data pointer
> from the encoded BTF member offset before rejecting the bitfield. The
> absent member zero check could then read outside the local struct_ops data
> and crash libbpf.
>
> Patch 1 rejects local bitfields before calculating the member data pointer.
> Patch 2 adds a negative test for a local bitfield absent from the kernel
> BTF type.
>
> [...]
Here is the summary with links:
- [bpf,v3,1/2] libbpf: Reject local struct_ops bitfields before data access
https://git.kernel.org/bpf/bpf-next/c/961b8946acb4
- [bpf,v3,2/2] selftests/bpf: Test local struct_ops bitfield rejection
(no matching commit)
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 6+ messages in thread