From: Mingpei CAO <caomingpei@gmail.com>
To: bpf@vger.kernel.org
Cc: andrii@kernel.org, eddyz87@gmail.com, ameryhung@gmail.com,
qmo@kernel.org, Mingpei CAO <caomingpei@gmail.com>
Subject: [PATCH bpf v2 2/3] bpftool: Keep struct_ops field offsets when skipping bitfields
Date: Sun, 13 Sep 2026 14:52:32 +0000 [thread overview]
Message-ID: <20260913145233.1446643-3-caomingpei@gmail.com> (raw)
In-Reply-To: <20260913145233.1446643-1-caomingpei@gmail.com>
For a bitfield, member->offset contains the bit position and field width.
The struct_ops skeleton generator used the full value as the bit position.
The generated C structure could contain excessive padding and place later
fields outside the map value.
Represent unsupported bitfield storage as padding in the generated
skeleton. This preserves the struct_ops map and keeps supported fields at
their declared BTF offsets. Validate every member range and alignment
before generating the C structure.
Fixes: a7b0fa352eaf ("bpftool: Generated shadow variables for struct_ops maps.")
Assisted-by: LLM
Signed-off-by: Mingpei CAO <caomingpei@gmail.com>
---
tools/bpf/bpftool/gen.c | 147 ++++++++++++++++++++++++++--------------
1 file changed, 97 insertions(+), 50 deletions(-)
diff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c
index a50540ef6521c..d93073d5e7e32 100644
--- a/tools/bpf/bpftool/gen.c
+++ b/tools/bpf/bpftool/gen.c
@@ -1044,62 +1044,79 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li
}
static int walk_st_ops_shadow_vars(struct btf *btf, const char *ident,
- const struct btf_type *map_type, __u32 map_type_id)
+ const struct btf_type *map_type)
{
+ enum st_ops_shadow_kind {
+ ST_OPS_SHADOW_SCALAR,
+ ST_OPS_SHADOW_FUNC_PTR,
+ ST_OPS_SHADOW_OPAQUE,
+ };
LIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts, .indent_level = 3);
const struct btf_type *member_type;
- __u32 offset, next_offset = 0;
+ __u64 bitfield_end = 0, map_bits;
+ __u32 bit_offset, bitfield_size;
+ __u32 map_size, offset, next_offset = 0;
const struct btf_member *m;
struct btf_dump *d = NULL;
const char *member_name;
__u32 member_type_id;
- int i, err = 0, n;
- int size;
+ int align, err = 0, i, max_align = 1, n, size;
+ enum st_ops_shadow_kind shadow_kind;
d = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL);
if (!d)
return -errno;
+ map_size = map_type->size;
+ map_bits = (__u64)map_size * 8;
n = btf_vlen(map_type);
for (i = 0, m = btf_members(map_type); i < n; i++, m++) {
- member_type = skip_mods_and_typedefs(btf, m->type, &member_type_id);
member_name = btf__name_by_offset(btf, m->name_off);
+ bit_offset = btf_member_bit_offset(map_type, i);
+ bitfield_size = btf_member_bitfield_size(map_type, i);
+ if (bitfield_size) {
+ if ((__u64)bit_offset < bitfield_end ||
+ (__u64)bit_offset < (__u64)next_offset * 8 ||
+ bit_offset > map_bits ||
+ bitfield_size > map_bits - bit_offset) {
+ p_err("Invalid bitfield layout for struct_ops member %s",
+ member_name);
+ err = -EINVAL;
+ goto out;
+ }
+ bitfield_end = (__u64)bit_offset + bitfield_size;
+ continue;
+ }
- offset = m->offset / 8;
- if (next_offset < offset)
- printf("\t\t\tchar __padding_%d[%u];\n", i, offset - next_offset);
+ if (bit_offset % 8 || bit_offset < bitfield_end) {
+ p_err("Invalid offset for struct_ops member %s", member_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ offset = bit_offset / 8;
+ if (offset < next_offset || offset > map_size) {
+ p_err("Invalid offset for struct_ops member %s", member_name);
+ err = -EINVAL;
+ goto out;
+ }
+ member_type = skip_mods_and_typedefs(btf, m->type, &member_type_id);
switch (btf_kind(member_type)) {
case BTF_KIND_INT:
case BTF_KIND_FLOAT:
case BTF_KIND_ENUM:
case BTF_KIND_ENUM64:
- /* scalar type */
- printf("\t\t\t");
- opts.field_name = member_name;
- err = btf_dump__emit_type_decl(d, member_type_id, &opts);
- if (err) {
- p_err("Failed to emit type declaration for %s: %d", member_name, err);
- goto out;
- }
- printf(";\n");
-
+ shadow_kind = ST_OPS_SHADOW_SCALAR;
size = btf__resolve_size(btf, member_type_id);
- if (size < 0) {
- p_err("Failed to resolve size of %s: %d\n", member_name, size);
- err = size;
- goto out;
- }
-
- next_offset = offset + size;
+ align = btf__align_of(btf, member_type_id);
break;
case BTF_KIND_PTR:
if (resolve_func_ptr(btf, m->type, NULL)) {
- /* Function pointer */
- printf("\t\t\tstruct bpf_program *%s;\n", member_name);
-
- next_offset = offset + sizeof(void *);
+ shadow_kind = ST_OPS_SHADOW_FUNC_PTR;
+ size = sizeof(void *);
+ align = __alignof__(void *);
break;
}
/* All pointer types are unsupported except for
@@ -1108,34 +1125,64 @@ static int walk_st_ops_shadow_vars(struct btf *btf, const char *ident,
fallthrough;
default:
- /* Unsupported types
- *
- * Types other than scalar types and function
- * pointers are currently not supported in order to
- * prevent conflicts in the generated code caused
- * by multiple definitions. For instance, if the
- * struct type FOO is used in a struct_ops map,
- * bpftool has to generate definitions for FOO,
- * which may result in conflicts if FOO is defined
- * in different skeleton files.
- */
+ shadow_kind = ST_OPS_SHADOW_OPAQUE;
size = btf__resolve_size(btf, member_type_id);
- if (size < 0) {
- p_err("Failed to resolve size of %s: %d\n", member_name, size);
- err = size;
+ align = 1;
+ break;
+ }
+
+ if (size < 0 || align < 0) {
+ err = size < 0 ? size : align;
+ p_err("Failed to resolve layout of %s: %d", member_name, err);
+ goto out;
+ }
+ if ((__u32)size > map_size - offset ||
+ (align > 1 && offset % align)) {
+ p_err("Invalid layout for struct_ops member %s", member_name);
+ err = -EINVAL;
+ goto out;
+ }
+
+ if (next_offset < offset)
+ printf("\t\t\tchar __padding_%d[%u];\n", i, offset - next_offset);
+
+ switch (shadow_kind) {
+ case ST_OPS_SHADOW_SCALAR:
+ printf("\t\t\t");
+ opts.field_name = member_name;
+ err = btf_dump__emit_type_decl(d, member_type_id, &opts);
+ if (err) {
+ p_err("Failed to emit type declaration for %s: %d",
+ member_name, err);
goto out;
}
+ printf(";\n");
+ break;
+ case ST_OPS_SHADOW_FUNC_PTR:
+ printf("\t\t\tstruct bpf_program *%s;\n", member_name);
+ break;
+ case ST_OPS_SHADOW_OPAQUE:
+ /* Avoid emitting named types that might conflict with
+ * definitions from other skeletons.
+ */
printf("\t\t\tchar __unsupported_%d[%d];\n", i, size);
-
- next_offset = offset + size;
break;
+ default:
+ __builtin_unreachable();
}
+
+ next_offset = offset + size;
+ max_align = max(max_align, align);
+ bitfield_end = 0;
}
- /* Cannot fail since it must be a struct type */
- size = btf__resolve_size(btf, map_type_id);
- if (next_offset < (__u32)size)
- printf("\t\t\tchar __padding_end[%u];\n", size - next_offset);
+ if (map_size % max_align) {
+ p_err("Invalid size for struct_ops type %s", ident);
+ err = -EINVAL;
+ goto out;
+ }
+ if (next_offset < map_size)
+ printf("\t\t\tchar __padding_end[%u];\n", map_size - next_offset);
out:
btf_dump__free(d);
@@ -1177,7 +1224,7 @@ static int gen_st_ops_shadow_type(const char *obj_name, struct btf *btf, const c
printf("\t\tstruct %s__%s__%s {\n", obj_name, ident, type_name);
- err = walk_st_ops_shadow_vars(btf, ident, map_type, map_type_id);
+ err = walk_st_ops_shadow_vars(btf, ident, map_type);
if (err)
return err;
--
2.43.0
next prev parent reply other threads:[~2026-09-13 14:52 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-13 14:52 [PATCH bpf v2 0/3] libbpf: Validate struct_ops member offsets Mingpei CAO
2026-09-13 14:52 ` [PATCH bpf v2 1/3] libbpf: Validate struct_ops member offsets before data access Mingpei CAO
2026-09-13 15:49 ` bot+bpf-ci
2026-09-13 14:52 ` Mingpei CAO [this message]
2026-09-13 15:02 ` [PATCH bpf v2 2/3] bpftool: Keep struct_ops field offsets when skipping bitfields sashiko-bot
2026-09-13 15:49 ` bot+bpf-ci
2026-09-13 14:52 ` [PATCH bpf v2 3/3] selftests/bpf: Cover struct_ops bitfield and offset validation Mingpei CAO
2026-09-13 15:03 ` sashiko-bot
2026-09-13 15:49 ` bot+bpf-ci
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260913145233.1446643-3-caomingpei@gmail.com \
--to=caomingpei@gmail.com \
--cc=ameryhung@gmail.com \
--cc=andrii@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=eddyz87@gmail.com \
--cc=qmo@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox