| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/09/13 15:05 | flow | patch-triage |
0mArgs:null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"KMSANReasoning": "",
"NeedsKMSAN": false,
"Reasoning": "The patch modifies only userspace code under tools/ (specifically bpftool in tools/bpf/bpftool, libbpf in tools/lib/bpf, and BPF selftests in tools/testing/selftests/bpf). None of the changes affect the compiled kernel image (vmlinux) or any kernel modules, so it does not warrant kernel fuzzing with syzkaller.",
"WorthFuzzing": false
} |
| 1/1 | 2026/09/13 15:05 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 2d018fb854e879ba3ff7f3154fc7cca7867de971\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Sun Sep 13 15:05:34 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/tools/bpf/bpftool/gen.c b/tools/bpf/bpftool/gen.c\nindex a50540ef6521c..d93073d5e7e32 100644\n--- a/tools/bpf/bpftool/gen.c\n+++ b/tools/bpf/bpftool/gen.c\n@@ -1044,62 +1044,79 @@ codegen_progs_skeleton(struct bpf_object *obj, size_t prog_cnt, bool populate_li\n }\n \n static int walk_st_ops_shadow_vars(struct btf *btf, const char *ident,\n-\t\t\t\t const struct btf_type *map_type, __u32 map_type_id)\n+\t\t\t\t const struct btf_type *map_type)\n {\n+\tenum st_ops_shadow_kind {\n+\t\tST_OPS_SHADOW_SCALAR,\n+\t\tST_OPS_SHADOW_FUNC_PTR,\n+\t\tST_OPS_SHADOW_OPAQUE,\n+\t};\n \tLIBBPF_OPTS(btf_dump_emit_type_decl_opts, opts, .indent_level = 3);\n \tconst struct btf_type *member_type;\n-\t__u32 offset, next_offset = 0;\n+\t__u64 bitfield_end = 0, map_bits;\n+\t__u32 bit_offset, bitfield_size;\n+\t__u32 map_size, offset, next_offset = 0;\n \tconst struct btf_member *m;\n \tstruct btf_dump *d = NULL;\n \tconst char *member_name;\n \t__u32 member_type_id;\n-\tint i, err = 0, n;\n-\tint size;\n+\tint align, err = 0, i, max_align = 1, n, size;\n+\tenum st_ops_shadow_kind shadow_kind;\n \n \td = btf_dump__new(btf, codegen_btf_dump_printf, NULL, NULL);\n \tif (!d)\n \t\treturn -errno;\n \n+\tmap_size = map_type-\u003esize;\n+\tmap_bits = (__u64)map_size * 8;\n \tn = btf_vlen(map_type);\n \tfor (i = 0, m = btf_members(map_type); i \u003c n; i++, m++) {\n-\t\tmember_type = skip_mods_and_typedefs(btf, m-\u003etype, \u0026member_type_id);\n \t\tmember_name = btf__name_by_offset(btf, m-\u003ename_off);\n+\t\tbit_offset = btf_member_bit_offset(map_type, i);\n+\t\tbitfield_size = btf_member_bitfield_size(map_type, i);\n+\t\tif (bitfield_size) {\n+\t\t\tif ((__u64)bit_offset \u003c bitfield_end ||\n+\t\t\t (__u64)bit_offset \u003c (__u64)next_offset * 8 ||\n+\t\t\t bit_offset \u003e map_bits ||\n+\t\t\t bitfield_size \u003e map_bits - bit_offset) {\n+\t\t\t\tp_err(\"Invalid bitfield layout for struct_ops member %s\",\n+\t\t\t\t member_name);\n+\t\t\t\terr = -EINVAL;\n+\t\t\t\tgoto out;\n+\t\t\t}\n+\t\t\tbitfield_end = (__u64)bit_offset + bitfield_size;\n+\t\t\tcontinue;\n+\t\t}\n \n-\t\toffset = m-\u003eoffset / 8;\n-\t\tif (next_offset \u003c offset)\n-\t\t\tprintf(\"\\t\\t\\tchar __padding_%d[%u];\\n\", i, offset - next_offset);\n+\t\tif (bit_offset % 8 || bit_offset \u003c bitfield_end) {\n+\t\t\tp_err(\"Invalid offset for struct_ops member %s\", member_name);\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\toffset = bit_offset / 8;\n+\t\tif (offset \u003c next_offset || offset \u003e map_size) {\n+\t\t\tp_err(\"Invalid offset for struct_ops member %s\", member_name);\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto out;\n+\t\t}\n \n+\t\tmember_type = skip_mods_and_typedefs(btf, m-\u003etype, \u0026member_type_id);\n \t\tswitch (btf_kind(member_type)) {\n \t\tcase BTF_KIND_INT:\n \t\tcase BTF_KIND_FLOAT:\n \t\tcase BTF_KIND_ENUM:\n \t\tcase BTF_KIND_ENUM64:\n-\t\t\t/* scalar type */\n-\t\t\tprintf(\"\\t\\t\\t\");\n-\t\t\topts.field_name = member_name;\n-\t\t\terr = btf_dump__emit_type_decl(d, member_type_id, \u0026opts);\n-\t\t\tif (err) {\n-\t\t\t\tp_err(\"Failed to emit type declaration for %s: %d\", member_name, err);\n-\t\t\t\tgoto out;\n-\t\t\t}\n-\t\t\tprintf(\";\\n\");\n-\n+\t\t\tshadow_kind = ST_OPS_SHADOW_SCALAR;\n \t\t\tsize = btf__resolve_size(btf, member_type_id);\n-\t\t\tif (size \u003c 0) {\n-\t\t\t\tp_err(\"Failed to resolve size of %s: %d\\n\", member_name, size);\n-\t\t\t\terr = size;\n-\t\t\t\tgoto out;\n-\t\t\t}\n-\n-\t\t\tnext_offset = offset + size;\n+\t\t\talign = btf__align_of(btf, member_type_id);\n \t\t\tbreak;\n \n \t\tcase BTF_KIND_PTR:\n \t\t\tif (resolve_func_ptr(btf, m-\u003etype, NULL)) {\n-\t\t\t\t/* Function pointer */\n-\t\t\t\tprintf(\"\\t\\t\\tstruct bpf_program *%s;\\n\", member_name);\n-\n-\t\t\t\tnext_offset = offset + sizeof(void *);\n+\t\t\t\tshadow_kind = ST_OPS_SHADOW_FUNC_PTR;\n+\t\t\t\tsize = sizeof(void *);\n+\t\t\t\talign = __alignof__(void *);\n \t\t\t\tbreak;\n \t\t\t}\n \t\t\t/* All pointer types are unsupported except for\n@@ -1108,34 +1125,64 @@ static int walk_st_ops_shadow_vars(struct btf *btf, const char *ident,\n \t\t\tfallthrough;\n \n \t\tdefault:\n-\t\t\t/* Unsupported types\n-\t\t\t *\n-\t\t\t * Types other than scalar types and function\n-\t\t\t * pointers are currently not supported in order to\n-\t\t\t * prevent conflicts in the generated code caused\n-\t\t\t * by multiple definitions. For instance, if the\n-\t\t\t * struct type FOO is used in a struct_ops map,\n-\t\t\t * bpftool has to generate definitions for FOO,\n-\t\t\t * which may result in conflicts if FOO is defined\n-\t\t\t * in different skeleton files.\n-\t\t\t */\n+\t\t\tshadow_kind = ST_OPS_SHADOW_OPAQUE;\n \t\t\tsize = btf__resolve_size(btf, member_type_id);\n-\t\t\tif (size \u003c 0) {\n-\t\t\t\tp_err(\"Failed to resolve size of %s: %d\\n\", member_name, size);\n-\t\t\t\terr = size;\n+\t\t\talign = 1;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (size \u003c 0 || align \u003c 0) {\n+\t\t\terr = size \u003c 0 ? size : align;\n+\t\t\tp_err(\"Failed to resolve layout of %s: %d\", member_name, err);\n+\t\t\tgoto out;\n+\t\t}\n+\t\tif ((__u32)size \u003e map_size - offset ||\n+\t\t (align \u003e 1 \u0026\u0026 offset % align)) {\n+\t\t\tp_err(\"Invalid layout for struct_ops member %s\", member_name);\n+\t\t\terr = -EINVAL;\n+\t\t\tgoto out;\n+\t\t}\n+\n+\t\tif (next_offset \u003c offset)\n+\t\t\tprintf(\"\\t\\t\\tchar __padding_%d[%u];\\n\", i, offset - next_offset);\n+\n+\t\tswitch (shadow_kind) {\n+\t\tcase ST_OPS_SHADOW_SCALAR:\n+\t\t\tprintf(\"\\t\\t\\t\");\n+\t\t\topts.field_name = member_name;\n+\t\t\terr = btf_dump__emit_type_decl(d, member_type_id, \u0026opts);\n+\t\t\tif (err) {\n+\t\t\t\tp_err(\"Failed to emit type declaration for %s: %d\",\n+\t\t\t\t member_name, err);\n \t\t\t\tgoto out;\n \t\t\t}\n+\t\t\tprintf(\";\\n\");\n+\t\t\tbreak;\n+\t\tcase ST_OPS_SHADOW_FUNC_PTR:\n+\t\t\tprintf(\"\\t\\t\\tstruct bpf_program *%s;\\n\", member_name);\n+\t\t\tbreak;\n+\t\tcase ST_OPS_SHADOW_OPAQUE:\n+\t\t\t/* Avoid emitting named types that might conflict with\n+\t\t\t * definitions from other skeletons.\n+\t\t\t */\n \t\t\tprintf(\"\\t\\t\\tchar __unsupported_%d[%d];\\n\", i, size);\n-\n-\t\t\tnext_offset = offset + size;\n \t\t\tbreak;\n+\t\tdefault:\n+\t\t\t__builtin_unreachable();\n \t\t}\n+\n+\t\tnext_offset = offset + size;\n+\t\tmax_align = max(max_align, align);\n+\t\tbitfield_end = 0;\n \t}\n \n-\t/* Cannot fail since it must be a struct type */\n-\tsize = btf__resolve_size(btf, map_type_id);\n-\tif (next_offset \u003c (__u32)size)\n-\t\tprintf(\"\\t\\t\\tchar __padding_end[%u];\\n\", size - next_offset);\n+\tif (map_size % max_align) {\n+\t\tp_err(\"Invalid size for struct_ops type %s\", ident);\n+\t\terr = -EINVAL;\n+\t\tgoto out;\n+\t}\n+\tif (next_offset \u003c map_size)\n+\t\tprintf(\"\\t\\t\\tchar __padding_end[%u];\\n\", map_size - next_offset);\n \n out:\n \tbtf_dump__free(d);\n@@ -1177,7 +1224,7 @@ static int gen_st_ops_shadow_type(const char *obj_name, struct btf *btf, const c\n \n \tprintf(\"\\t\\tstruct %s__%s__%s {\\n\", obj_name, ident, type_name);\n \n-\terr = walk_st_ops_shadow_vars(btf, ident, map_type, map_type_id);\n+\terr = walk_st_ops_shadow_vars(btf, ident, map_type);\n \tif (err)\n \t\treturn err;\n \ndiff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c\nindex b749c01742ee0..2791b48585939 100644\n--- a/tools/lib/bpf/libbpf.c\n+++ b/tools/lib/bpf/libbpf.c\n@@ -1175,6 +1175,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)\n \tconst struct btf_member *member, *kern_member, *kern_data_member;\n \tconst struct btf_type *type, *kern_type, *kern_vtype;\n \t__u32 i, kern_type_id, kern_vtype_id, kern_data_off;\n+\t__u32 kern_data_bit_off, kern_data_member_idx;\n \tstruct bpf_object *obj = map-\u003eobj;\n \tconst struct btf *btf = obj-\u003ebtf;\n \tstruct bpf_struct_ops *st_ops;\n@@ -1195,6 +1196,25 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)\n \t\treturn err;\n \n \tkern_btf = mod_btf ? mod_btf-\u003ebtf : obj-\u003ebtf_vmlinux;\n+\tkern_data_member_idx = kern_data_member - btf_members(kern_vtype);\n+\tif (btf_member_bitfield_size(kern_vtype, kern_data_member_idx)) {\n+\t\tpr_warn(\"struct_ops init_kern %s: kernel data member is a bitfield\\n\",\n+\t\t\tmap-\u003ename);\n+\t\treturn -ENOTSUP;\n+\t}\n+\tkern_data_bit_off = btf_member_bit_offset(kern_vtype, kern_data_member_idx);\n+\tif (kern_data_bit_off % 8) {\n+\t\tpr_warn(\"struct_ops init_kern %s: kernel data member has a non-byte-aligned offset\\n\",\n+\t\t\tmap-\u003ename);\n+\t\treturn -EINVAL;\n+\t}\n+\tkern_data_off = kern_data_bit_off / 8;\n+\tif (kern_data_off \u003e kern_vtype-\u003esize ||\n+\t kern_type-\u003esize \u003e kern_vtype-\u003esize - kern_data_off) {\n+\t\tpr_warn(\"struct_ops init_kern %s: kernel data member is outside the %u-byte kernel value type\\n\",\n+\t\t\tmap-\u003ename, kern_vtype-\u003esize);\n+\t\treturn -EINVAL;\n+\t}\n \n \tpr_debug(\"struct_ops init_kern %s: type_id:%u kern_type_id:%u kern_vtype_id:%u\\n\",\n \t\t map-\u003ename, st_ops-\u003etype_id, kern_type_id, kern_vtype_id);\n@@ -1208,29 +1228,50 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)\n \t\treturn -ENOMEM;\n \n \tdata = st_ops-\u003edata;\n-\tkern_data_off = kern_data_member-\u003eoffset / 8;\n \tkern_data = st_ops-\u003ekern_vdata + kern_data_off;\n \n \tmember = btf_members(type);\n \tfor (i = 0; i \u003c btf_vlen(type); i++, member++) {\n \t\tconst struct btf_type *mtype, *kern_mtype;\n-\t\t__u32 mtype_id, kern_mtype_id;\n+\t\t__u32 bit_offset, mtype_id, kern_mtype_id;\n \t\tvoid *mdata, *kern_mdata;\n \t\tstruct bpf_program *prog;\n \t\t__s64 msize, kern_msize;\n+\t\t__u64 maccess_size;\n \t\t__u32 moff, kern_moff;\n \t\t__u32 kern_member_idx;\n \t\tconst char *mname;\n \n \t\tmname = btf__name_by_offset(btf, member-\u003ename_off);\n-\t\tmoff = member-\u003eoffset / 8;\n-\t\tmdata = data + moff;\n+\t\tif (btf_member_bitfield_size(type, i)) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: local bitfield %s is not supported\\n\",\n+\t\t\t\tmap-\u003ename, mname);\n+\t\t\treturn -ENOTSUP;\n+\t\t}\n+\n \t\tmsize = btf__resolve_size(btf, member-\u003etype);\n \t\tif (msize \u003c 0) {\n \t\t\tpr_warn(\"struct_ops init_kern %s: failed to resolve the size of member %s\\n\",\n \t\t\t\tmap-\u003ename, mname);\n \t\t\treturn msize;\n \t\t}\n+\t\tmtype = skip_mods_and_typedefs(btf, member-\u003etype, \u0026mtype_id);\n+\t\tmaccess_size = msize;\n+\t\tif (btf_is_ptr(mtype) \u0026\u0026 maccess_size \u003c sizeof(prog))\n+\t\t\tmaccess_size = sizeof(prog);\n+\t\tbit_offset = btf_member_bit_offset(type, i);\n+\t\tif (bit_offset % 8) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: member %s has a non-byte-aligned offset\\n\",\n+\t\t\t\tmap-\u003ename, mname);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tmoff = bit_offset / 8;\n+\t\tif (moff \u003e type-\u003esize || maccess_size \u003e type-\u003esize - moff) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: member %s is outside the %u-byte local struct_ops type\\n\",\n+\t\t\t\tmap-\u003ename, mname, type-\u003esize);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tmdata = data + moff;\n \n \t\tkern_member = find_member_by_name(kern_btf, kern_type, mname);\n \t\tif (!kern_member) {\n@@ -1259,17 +1300,33 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)\n \t\t}\n \n \t\tkern_member_idx = kern_member - btf_members(kern_type);\n-\t\tif (btf_member_bitfield_size(type, i) ||\n-\t\t btf_member_bitfield_size(kern_type, kern_member_idx)) {\n-\t\t\tpr_warn(\"struct_ops init_kern %s: bitfield %s is not supported\\n\",\n+\t\tif (btf_member_bitfield_size(kern_type, kern_member_idx)) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: kernel bitfield %s is not supported\\n\",\n \t\t\t\tmap-\u003ename, mname);\n \t\t\treturn -ENOTSUP;\n \t\t}\n \n-\t\tkern_moff = kern_member-\u003eoffset / 8;\n+\t\tkern_msize = btf__resolve_size(kern_btf, kern_member-\u003etype);\n+\t\tif (kern_msize \u003c 0) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: failed to resolve the kernel size of member %s\\n\",\n+\t\t\t\tmap-\u003ename, mname);\n+\t\t\treturn kern_msize;\n+\t\t}\n+\t\tbit_offset = btf_member_bit_offset(kern_type, kern_member_idx);\n+\t\tif (bit_offset % 8) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: kernel member %s has a non-byte-aligned offset\\n\",\n+\t\t\t\tmap-\u003ename, mname);\n+\t\t\treturn -EINVAL;\n+\t\t}\n+\t\tkern_moff = bit_offset / 8;\n+\t\tif (kern_moff \u003e kern_type-\u003esize ||\n+\t\t (__u64)kern_msize \u003e kern_type-\u003esize - kern_moff) {\n+\t\t\tpr_warn(\"struct_ops init_kern %s: kernel member %s is outside the %u-byte kernel struct_ops type\\n\",\n+\t\t\t\tmap-\u003ename, mname, kern_type-\u003esize);\n+\t\t\treturn -EINVAL;\n+\t\t}\n \t\tkern_mdata = kern_data + kern_moff;\n \n-\t\tmtype = skip_mods_and_typedefs(btf, member-\u003etype, \u0026mtype_id);\n \t\tkern_mtype = skip_mods_and_typedefs(kern_btf, kern_member-\u003etype,\n \t\t\t\t\t\t \u0026kern_mtype_id);\n \t\tif (BTF_INFO_KIND(mtype-\u003einfo) !=\n@@ -1281,7 +1338,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)\n \t\t}\n \n \t\tif (btf_is_ptr(mtype)) {\n-\t\t\tprog = *(void **)mdata;\n+\t\t\tmemcpy(\u0026prog, mdata, sizeof(prog));\n \t\t\t/* just like for !kern_member case above, reset declaratively\n \t\t\t * set (at compile time) program's autload to false,\n \t\t\t * if user replaced it with another program or NULL\n@@ -1352,8 +1409,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)\n \t\t\tcontinue;\n \t\t}\n \n-\t\tkern_msize = btf__resolve_size(kern_btf, kern_mtype_id);\n-\t\tif (kern_msize \u003c 0 || msize != kern_msize) {\n+\t\tif (msize != kern_msize) {\n \t\t\tpr_warn(\"struct_ops init_kern %s: Error in size of member %s: %zd != %zd(kernel)\\n\",\n \t\t\t\tmap-\u003ename, mname, (ssize_t)msize,\n \t\t\t\t(ssize_t)kern_msize);\ndiff --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\nindex 75a0dea511b3f..41029ebb22f01 100644\n--- a/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c\n+++ b/tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c\n@@ -1,6 +1,7 @@\n // SPDX-License-Identifier: GPL-2.0\n /* Copyright (c) 2024 Meta Platforms, Inc. and affiliates. */\n #include \u003ctest_progs.h\u003e\n+#include \u003cbpf/btf.h\u003e\n #include \u003ctime.h\u003e\n \n #include \u003csys/epoll.h\u003e\n@@ -150,6 +151,91 @@ static void test_struct_ops_not_zeroed(void)\n \tstruct_ops_module__destroy(skel);\n }\n \n+static void test_struct_ops_local_bitfield(void)\n+{\n+\tstruct struct_ops_module *skel;\n+\tchar *log = NULL;\n+\tint err;\n+\n+\tskel = struct_ops_module__open();\n+\tif (!ASSERT_OK_PTR(skel, \"struct_ops_module_open_local_bitfield\"))\n+\t\treturn;\n+\n+\tif (!ASSERT_EQ(offsetof(typeof(*skel-\u003estruct_ops.testmod_local_bitfield), data),\n+\t\t 0, \"local_bitfield_data_offset\") ||\n+\t !ASSERT_EQ(offsetof(typeof(*skel-\u003estruct_ops.testmod_local_bitfield), onebyte),\n+\t\t 8, \"local_bitfield_onebyte_offset\"))\n+\t\tgoto cleanup;\n+\tif (!ASSERT_EQ(sizeof(*skel-\u003estruct_ops.testmod_local_bitfield),\n+\t\t bpf_map__value_size(skel-\u003emaps.testmod_local_bitfield),\n+\t\t \"local_bitfield_shadow_size\"))\n+\t\tgoto cleanup;\n+\n+\t/* Normal fields around the unsupported bitfield remain accessible. */\n+\tskel-\u003estruct_ops.testmod_local_bitfield-\u003edata = 1;\n+\tskel-\u003estruct_ops.testmod_local_bitfield-\u003eonebyte = 1;\n+\n+\terr = bpf_map__set_autocreate(skel-\u003emaps.testmod_local_bitfield, true);\n+\tif (!ASSERT_OK(err, \"enable_local_bitfield_map\"))\n+\t\tgoto cleanup;\n+\n+\tif (start_libbpf_log_capture())\n+\t\tgoto cleanup;\n+\terr = struct_ops_module__load(skel);\n+\tlog = stop_libbpf_log_capture();\n+\tif (!ASSERT_EQ(err, -ENOTSUP, \"struct_ops_module_load_local_bitfield\"))\n+\t\tgoto cleanup;\n+\tASSERT_HAS_SUBSTR(log, \"local bitfield extra_bitfield is not supported\",\n+\t\t\t \"local_bitfield_rejection_log\");\n+\n+cleanup:\n+\tfree(log);\n+\tstruct_ops_module__destroy(skel);\n+}\n+\n+static void test_struct_ops_bad_member_offset(void)\n+{\n+\tstruct struct_ops_module *skel;\n+\tstruct btf_member *member;\n+\tstruct btf_type *type;\n+\tstruct btf *btf;\n+\tchar *log = NULL;\n+\tint err, type_id;\n+\n+\tskel = struct_ops_module__open();\n+\tif (!ASSERT_OK_PTR(skel, \"struct_ops_module_open_bad_offset\"))\n+\t\treturn;\n+\n+\tbtf = bpf_object__btf(skel-\u003eobj);\n+\ttype_id = btf__find_by_name_kind(btf, \"bpf_testmod_ops___bad_offset\",\n+\t\t\t\t\t BTF_KIND_STRUCT);\n+\tif (!ASSERT_GT(type_id, 0, \"find_bad_offset_type\"))\n+\t\tgoto cleanup;\n+\ttype = (struct btf_type *)btf__type_by_id(btf, type_id);\n+\tif (!ASSERT_OK_PTR(type, \"get_bad_offset_type\"))\n+\t\tgoto cleanup;\n+\n+\tmember = btf_members(type);\n+\tmember-\u003eoffset = type-\u003esize * 8;\n+\n+\terr = bpf_map__set_autocreate(skel-\u003emaps.testmod_bad_offset, true);\n+\tif (!ASSERT_OK(err, \"enable_bad_offset_map\"))\n+\t\tgoto cleanup;\n+\n+\tif (start_libbpf_log_capture())\n+\t\tgoto cleanup;\n+\terr = struct_ops_module__load(skel);\n+\tlog = stop_libbpf_log_capture();\n+\tif (!ASSERT_EQ(err, -EINVAL, \"struct_ops_module_load_bad_offset\"))\n+\t\tgoto cleanup;\n+\tASSERT_HAS_SUBSTR(log, \"member extra is outside the 4-byte local struct_ops type\",\n+\t\t\t \"bad_offset_rejection_log\");\n+\n+cleanup:\n+\tfree(log);\n+\tstruct_ops_module__destroy(skel);\n+}\n+\n /* The signature of an implementation might not match the signature of the\n * function pointer prototype defined in the BPF program. This mismatch\n * should be allowed as long as the behavior of the operator program\n@@ -304,6 +390,10 @@ void serial_test_struct_ops_module(void)\n \t\ttest_struct_ops_load();\n \tif (test__start_subtest(\"struct_ops_not_zeroed\"))\n \t\ttest_struct_ops_not_zeroed();\n+\tif (test__start_subtest(\"struct_ops_local_bitfield\"))\n+\t\ttest_struct_ops_local_bitfield();\n+\tif (test__start_subtest(\"struct_ops_bad_member_offset\"))\n+\t\ttest_struct_ops_bad_member_offset();\n \tif (test__start_subtest(\"struct_ops_incompatible\"))\n \t\ttest_struct_ops_incompatible();\n \tif (test__start_subtest(\"struct_ops_null_out_cb\"))\n@@ -314,4 +404,3 @@ void serial_test_struct_ops_module(void)\n \t\ttest_detach_link();\n \tRUN_TESTS(unsupported_ops);\n }\n-\ndiff --git a/tools/testing/selftests/bpf/progs/struct_ops_module.c b/tools/testing/selftests/bpf/progs/struct_ops_module.c\nindex 71c420c3a5a6c..846b5a934dc83 100644\n--- a/tools/testing/selftests/bpf/progs/struct_ops_module.c\n+++ b/tools/testing/selftests/bpf/progs/struct_ops_module.c\n@@ -76,6 +76,23 @@ struct bpf_testmod_ops___zeroed testmod_zeroed = {\n \t.zeroed_op = (void *)zeroed_op,\n };\n \n+struct bpf_testmod_ops___local_bitfield {\n+\tint data;\n+\tunsigned int extra_bitfield:31;\n+\tunsigned int extra_bitfield2:1;\n+\tchar onebyte;\n+};\n+\n+SEC(\"?.struct_ops.link\")\n+struct bpf_testmod_ops___local_bitfield testmod_local_bitfield;\n+\n+struct bpf_testmod_ops___bad_offset {\n+\tunsigned int extra;\n+};\n+\n+SEC(\"?.struct_ops.link\")\n+struct bpf_testmod_ops___bad_offset testmod_bad_offset;\n+\n struct bpf_testmod_ops___incompatible {\n \tint (*test_1)(void);\n \tvoid (*test_2)(int *a);\n"
} |
| 2/1 | 2026/09/13 15:05 | agent | patch-evaluator |
0mModel:core Args: null Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies only userspace code under tools/ (specifically bpftool in tools/bpf/bpftool, libbpf in tools/lib/bpf, and BPF selftests in tools/testing/selftests/bpf). None of the changes affect the compiled kernel image (vmlinux) or any kernel modules, so it does not warrant kernel fuzzing with syzkaller.",
"WorthFuzzing": false
}Instruction: You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.
IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.
================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.
- REACHABILITY IS THE PRIMARY GATE:
Fuzzing can only discover bugs in code that can actually execute in standard virtualized
environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
regardless of whether it adds assertions or complex logic.
- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
Patch authors routinely label changes as "cleanups", "refactorings", or state
"No functional change intended". Do NOT take these claims at face value.
Code refactorings that rearrange logic, introduce helper functions, or alter state management
in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
If reachable executable code is modified or refactored, it MUST be fuzzed.
- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
Even if the author believes the invariant always holds, fuzzing is essential to verify whether
an unusual sequence of operations can violate it.
================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:
- Non-kernel and non-executable changes:
* Modifications to Documentation/, comments, or spelling fixes.
* User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
that do not affect the compiled kernel image (vmlinux) or kernel modules.
* Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
that do not alter control flow or data structures.
* Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
* Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
* Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
physical PCI hot-unplug or manual sysfs driver unbinding.
* Code paths exclusive to architectures other than the target architecture.
================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
* Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
* Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
* Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
* Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).
================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:
- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
Do NOT list generic, ubiquitous functions called by almost every program in the corpus
(including, but not limited to: general memory allocators and deallocators, page fault
and trap handlers, or core synchronization primitives; this is not an exhaustive list).
Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
which severely dilutes fuzzing effort away from the actual changes.
- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
List functions that specifically implement the logic being added or altered, or direct API entrypoints
for the subsystem feature under review.
- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).
================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt: Target architecture: amd64
For your convenience, here is the diff of the changes:
commit 2d018fb854e879ba3ff7f3154fc7cca7867de971
Author: syz-cluster <triage@syzkaller.com>
Date: Sun Sep 13 15:05:34 2026 +0000
syz-cluster: applied patch under review
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;
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index b749c01742ee0..2791b48585939 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -1175,6 +1175,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
const struct btf_member *member, *kern_member, *kern_data_member;
const struct btf_type *type, *kern_type, *kern_vtype;
__u32 i, kern_type_id, kern_vtype_id, kern_data_off;
+ __u32 kern_data_bit_off, kern_data_member_idx;
struct bpf_object *obj = map->obj;
const struct btf *btf = obj->btf;
struct bpf_struct_ops *st_ops;
@@ -1195,6 +1196,25 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
return err;
kern_btf = mod_btf ? mod_btf->btf : obj->btf_vmlinux;
+ kern_data_member_idx = kern_data_member - btf_members(kern_vtype);
+ if (btf_member_bitfield_size(kern_vtype, kern_data_member_idx)) {
+ pr_warn("struct_ops init_kern %s: kernel data member is a bitfield\n",
+ map->name);
+ return -ENOTSUP;
+ }
+ kern_data_bit_off = btf_member_bit_offset(kern_vtype, kern_data_member_idx);
+ if (kern_data_bit_off % 8) {
+ pr_warn("struct_ops init_kern %s: kernel data member has a non-byte-aligned offset\n",
+ map->name);
+ return -EINVAL;
+ }
+ kern_data_off = kern_data_bit_off / 8;
+ if (kern_data_off > kern_vtype->size ||
+ kern_type->size > kern_vtype->size - kern_data_off) {
+ pr_warn("struct_ops init_kern %s: kernel data member is outside the %u-byte kernel value type\n",
+ map->name, kern_vtype->size);
+ return -EINVAL;
+ }
pr_debug("struct_ops init_kern %s: type_id:%u kern_type_id:%u kern_vtype_id:%u\n",
map->name, st_ops->type_id, kern_type_id, kern_vtype_id);
@@ -1208,29 +1228,50 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
return -ENOMEM;
data = st_ops->data;
- kern_data_off = kern_data_member->offset / 8;
kern_data = st_ops->kern_vdata + kern_data_off;
member = btf_members(type);
for (i = 0; i < btf_vlen(type); i++, member++) {
const struct btf_type *mtype, *kern_mtype;
- __u32 mtype_id, kern_mtype_id;
+ __u32 bit_offset, mtype_id, kern_mtype_id;
void *mdata, *kern_mdata;
struct bpf_program *prog;
__s64 msize, kern_msize;
+ __u64 maccess_size;
__u32 moff, kern_moff;
__u32 kern_member_idx;
const char *mname;
mname = btf__name_by_offset(btf, member->name_off);
- moff = member->offset / 8;
- mdata = data + moff;
+ 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;
+ }
+
msize = btf__resolve_size(btf, member->type);
if (msize < 0) {
pr_warn("struct_ops init_kern %s: failed to resolve the size of member %s\n",
map->name, mname);
return msize;
}
+ mtype = skip_mods_and_typedefs(btf, member->type, &mtype_id);
+ maccess_size = msize;
+ if (btf_is_ptr(mtype) && maccess_size < sizeof(prog))
+ maccess_size = sizeof(prog);
+ bit_offset = btf_member_bit_offset(type, i);
+ if (bit_offset % 8) {
+ pr_warn("struct_ops init_kern %s: member %s has a non-byte-aligned offset\n",
+ map->name, mname);
+ return -EINVAL;
+ }
+ moff = bit_offset / 8;
+ if (moff > type->size || maccess_size > type->size - moff) {
+ pr_warn("struct_ops init_kern %s: member %s is outside the %u-byte local struct_ops type\n",
+ map->name, mname, type->size);
+ return -EINVAL;
+ }
+ mdata = data + moff;
kern_member = find_member_by_name(kern_btf, kern_type, mname);
if (!kern_member) {
@@ -1259,17 +1300,33 @@ 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)) {
- pr_warn("struct_ops init_kern %s: bitfield %s is not supported\n",
+ if (btf_member_bitfield_size(kern_type, kern_member_idx)) {
+ pr_warn("struct_ops init_kern %s: kernel bitfield %s is not supported\n",
map->name, mname);
return -ENOTSUP;
}
- kern_moff = kern_member->offset / 8;
+ kern_msize = btf__resolve_size(kern_btf, kern_member->type);
+ if (kern_msize < 0) {
+ pr_warn("struct_ops init_kern %s: failed to resolve the kernel size of member %s\n",
+ map->name, mname);
+ return kern_msize;
+ }
+ bit_offset = btf_member_bit_offset(kern_type, kern_member_idx);
+ if (bit_offset % 8) {
+ pr_warn("struct_ops init_kern %s: kernel member %s has a non-byte-aligned offset\n",
+ map->name, mname);
+ return -EINVAL;
+ }
+ kern_moff = bit_offset / 8;
+ if (kern_moff > kern_type->size ||
+ (__u64)kern_msize > kern_type->size - kern_moff) {
+ pr_warn("struct_ops init_kern %s: kernel member %s is outside the %u-byte kernel struct_ops type\n",
+ map->name, mname, kern_type->size);
+ return -EINVAL;
+ }
kern_mdata = kern_data + kern_moff;
- mtype = skip_mods_and_typedefs(btf, member->type, &mtype_id);
kern_mtype = skip_mods_and_typedefs(kern_btf, kern_member->type,
&kern_mtype_id);
if (BTF_INFO_KIND(mtype->info) !=
@@ -1281,7 +1338,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
}
if (btf_is_ptr(mtype)) {
- prog = *(void **)mdata;
+ memcpy(&prog, mdata, sizeof(prog));
/* just like for !kern_member case above, reset declaratively
* set (at compile time) program's autload to false,
* if user replaced it with another program or NULL
@@ -1352,8 +1409,7 @@ static int bpf_map__init_kern_struct_ops(struct bpf_map *map)
continue;
}
- kern_msize = btf__resolve_size(kern_btf, kern_mtype_id);
- if (kern_msize < 0 || msize != kern_msize) {
+ if (msize != kern_msize) {
pr_warn("struct_ops init_kern %s: Error in size of member %s: %zd != %zd(kernel)\n",
map->name, mname, (ssize_t)msize,
(ssize_t)kern_msize);
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 <test_progs.h>
+#include <bpf/btf.h>
#include <time.h>
#include <sys/epoll.h>
@@ -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);
|
| 3/2 | 2026/09/13 15:05 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 9897 output: 30 thoughts: 370 Thoughts: **My Initial Assessment of the Syzkaller Patch Series** Okay, the user wants me to vet this patch series for syzkaller fuzzing. My immediate move is to figure out the scope of the change. Let's see what files were even touched. The diff highlights: `tools/bpf/bpftool/gen.c`, `tools/lib/bpf/libbpf.c`, `tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c`, and `tools/testing/selftests/bpf/progs/struct_ops_module.c`. Wait, everything is in `tools/`! This is starting to look suspicious. Before I get ahead of myself, let's verify if that's *truly* the case. I need to confirm whether any other files were modified in `HEAD` using `git` if possible, or by carefully examining the diff output itself. Okay, hold on, let me quickly refer to Section 2 of the prompt. We're looking for reasons to reject this. It says I should flag it as `WorthFuzzing=false` only if *all* modified code falls under specific non-kernel categories. These include documentation updates, user-space changes like self-tests or scripts that *don't* impact the kernel image, purely decorative logging, or build/Kconfig changes that don't change compiled logic. So, since all changes are in `tools/`, I'll check and ensure those files don't trigger anything negative based on the prompt's criteria. I need to be *sure* that there are no hidden changes anywhere else. I should run a tool or script to double-check `git diff` or the `log` to avoid any oversight. |
| 4/2 | 2026/09/13 15:05 | tool | grepper |
0mError:no matches Args: {
"Expression": "walk_st_ops_shadow_vars",
"PathPrefix": "kernel"
}Results: {
"error": "no matches"
} |
| 5/2 | 2026/09/13 15:05 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 10695 output: 117 thoughts: 643 Thoughts: **Analysis of the Proposed Patch's Fuzzing Applicability** Okay, let's break this down. First, I need to verify where these file modifications reside. I see `tools/bpf/bpftool/gen.c`, `tools/lib/bpf/libbpf.c`, `tools/testing/selftests/bpf/prog_tests/test_struct_ops_module.c`, and `tools/testing/selftests/bpf/progs/struct_ops_module.c`. Crucially, all of these are within the `tools/` directory. That's a strong indicator. Now, I'm thinking, `tools/bpf/bpftool` is a user-space tool. `tools/lib/bpf` is the user-space libbpf library. `tools/testing/selftests` obviously holds user-space self-tests. None of this directly touches anything core to the kernel: no `kernel/`, no `net/`, no `drivers/`, no `fs/`, and no `include/` that would affect the kernel's compilation into `vmlinux` or a kernel module. Just to be absolutely sure, I'll confirm that libbpf is not compiled into the kernel. Yep, user-space library it is. The kernel's BPF verifier and `struct_ops` implementation live in `kernel/bpf/`. This patch *only* touches the user-space directories, `tools/bpf/bpftool/`, `tools/lib/bpf/`, and `tools/testing/selftests/bpf/`. According to Section 2 of the guidelines, this is a clear case: "User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/) that do not affect the compiled kernel image (vmlinux) or kernel modules." So, I can confidently set `WorthFuzzing` to `false`. Given `WorthFuzzing=false`, let's check the parameter descriptions. `EnableConfigs` should be empty, `FocusSymbols` should be empty, and the `Reasoning` should be crystal clear. For `WorthFuzzing=false`, `EnableConfigs` should be `[]` (or null), `FocusSymbols` will be `[]`, and the `Reasoning` will have to explain that all changes are confined to userspace tools, libraries, and selftests under `tools/` (specifically `tools/bpf/bpftool/`, `tools/lib/bpf/`, and `tools/testing/selftests/`), and do not affect the compiled kernel image (`vmlinux`) or kernel modules. Thus, this patch is not applicable for syzkaller kernel fuzzing. I've got it. No subtleties. This is a straightforward negative case. I'm ready to call `set-results`. |
| 6/2 | 2026/09/13 15:06 | tool | set-results |
0mArgs:{
"EnableConfigs": [],
"FocusSymbols": [],
"Reasoning": "The patch modifies only userspace code under tools/ (specifically bpftool in tools/bpf/bpftool, libbpf in tools/lib/bpf, and BPF selftests in tools/testing/selftests/bpf). None of the changes affect the compiled kernel image (vmlinux) or any kernel modules, so it does not warrant kernel fuzzing with syzkaller.",
"WorthFuzzing": false
}Results: {
"EnableConfigs": null,
"FocusSymbols": [],
"Reasoning": "The patch modifies only userspace code under tools/ (specifically bpftool in tools/bpf/bpftool, libbpf in tools/lib/bpf, and BPF selftests in tools/testing/selftests/bpf). None of the changes affect the compiled kernel image (vmlinux) or any kernel modules, so it does not warrant kernel fuzzing with syzkaller.",
"WorthFuzzing": false
} |
| 7/1 | 2026/09/13 15:06 | action | If |
0mArgs:{
"WorthFuzzing": false
}Results: null |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|