AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/13 04:19 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch only modifies the userspace libbpf library (tools/lib/bpf) and adds selftests. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

1/1 2026/08/13 04:19 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 6aa8f7601b07fdaa3fc744696acb256b09983592\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Aug 13 04:19:09 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h\nindex 9d160b5b9c0ee..171ea055cd323 100644\n--- a/tools/lib/bpf/bpf_helpers.h\n+++ b/tools/lib/bpf/bpf_helpers.h\n@@ -188,6 +188,20 @@ enum libbpf_tristate {\n \tTRI_MODULE = 2,\n };\n \n+/* Helper typedef for declaring kernel module names that need BTF loading.\n+ *\n+ * Usage: define an array in the \".kmod_btfs\" ELF section to specify\n+ * which modules need BTF loading:\n+ *\n+ *   DEFINE_KMOD_BTFS(_needed_kmods)= { \"module1\", \"module2\", ... };\n+ *\n+ * This avoids unnecessary BTF loading and speeds up the BPF program\n+ * load process.\n+ */\n+#define KMOD_NAME_LEN 64\n+#define DEFINE_KMOD_BTFS(name) \\\n+\tSEC(\".kmod_btfs\") char name[][KMOD_NAME_LEN]\n+\n #define __kconfig __attribute__((section(\".kconfig\")))\n #define __ksym __attribute__((section(\".ksyms\")))\n #define __kptr_untrusted __attribute__((btf_type_tag(\"kptr_untrusted\")))\ndiff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c\nindex 514e4e9daa825..9fe8a653e8b6b 100644\n--- a/tools/lib/bpf/libbpf.c\n+++ b/tools/lib/bpf/libbpf.c\n@@ -548,6 +548,7 @@ struct bpf_struct_ops {\n #define STRUCT_OPS_SEC \".struct_ops\"\n #define STRUCT_OPS_LINK_SEC \".struct_ops.link\"\n #define ARENA_SEC \".addr_space.1\"\n+#define KMODS_BTFS_SEC \".kmod_btfs\"\n \n enum libbpf_map_type {\n \tLIBBPF_MAP_UNSPEC,\n@@ -703,6 +704,9 @@ enum bpf_object_state {\n \tOBJ_LOADED,\n };\n \n+/* Should match the definition in bpf_helpers.h */\n+#define KMOD_NAME_LEN 64\n+\n struct bpf_object {\n \tchar name[BPF_OBJ_NAME_LEN];\n \tchar license[64];\n@@ -779,6 +783,14 @@ struct bpf_object {\n \tchar *token_path;\n \tint token_fd;\n \n+\t/* kernel module BTFs to load, declared in \".kmod_btfs\" ELF section */\n+\tstruct {\n+\t\tchar (*data)[KMOD_NAME_LEN];\n+\t\tsize_t nr_names;\n+\t\tsize_t nr_loaded;\n+\t\tstruct hashmap *hashmap;\n+\t} *kmod_btfs;\n+\n \tchar path[];\n };\n \n@@ -901,6 +913,75 @@ bpf_object__init_prog(struct bpf_object *obj, struct bpf_program *prog,\n \treturn -ENOMEM;\n }\n \n+static size_t mod_name_hash_fn(long key, void *ctx)\n+{\n+\treturn str_hash((char *)key);\n+}\n+\n+static bool mod_name_equal_fn(long key1, long key2, void *ctx)\n+{\n+\treturn strcmp((char *)key1, (char *)key2) == 0;\n+}\n+\n+static int\n+bpf_object__collect_kmod_btf_names(struct bpf_object *obj, Elf_Data *sec_data,\n+\t\t\t\t   const char *sec_name)\n+{\n+\tint module_cnt, i, err = 0;\n+\n+\tif (obj-\u003ekmod_btfs) {\n+\t\tpr_warn(\"sec '%s': duplicate sections detected\\n\", sec_name);\n+\t\treturn -EEXIST;\n+\t}\n+\n+\tif (sec_data-\u003ed_size % KMOD_NAME_LEN != 0) {\n+\t\tpr_warn(\"sec '%s': size %zu should be multiple of %d\\n\",\n+\t\t\tsec_name, sec_data-\u003ed_size, KMOD_NAME_LEN);\n+\t\treturn -EINVAL;\n+\t}\n+\n+\tmodule_cnt = sec_data-\u003ed_size / KMOD_NAME_LEN;\n+\tobj-\u003ekmod_btfs = calloc(1, sizeof(*obj-\u003ekmod_btfs));\n+\tif (!obj-\u003ekmod_btfs)\n+\t\treturn -ENOMEM;\n+\n+\tobj-\u003ekmod_btfs-\u003edata = calloc(module_cnt, KMOD_NAME_LEN);\n+\tif (!obj-\u003ekmod_btfs-\u003edata) {\n+\t\terr = -ENOMEM;\n+\t\tgoto err_out;\n+\t}\n+\tmemcpy(obj-\u003ekmod_btfs-\u003edata, sec_data-\u003ed_buf, sec_data-\u003ed_size);\n+\n+\tobj-\u003ekmod_btfs-\u003ehashmap = hashmap__new(mod_name_hash_fn,\n+\t\t\t\t\t       mod_name_equal_fn, NULL);\n+\tif (IS_ERR(obj-\u003ekmod_btfs-\u003ehashmap)) {\n+\t\terr = PTR_ERR(obj-\u003ekmod_btfs-\u003ehashmap);\n+\t\tgoto err_out;\n+\t}\n+\n+\tfor (i = 0; i \u003c module_cnt; i++) {\n+\t\tobj-\u003ekmod_btfs-\u003edata[i][KMOD_NAME_LEN - 1] = '\\0';\n+\t\tif (hashmap__find(obj-\u003ekmod_btfs-\u003ehashmap,\n+\t\t\t\t  obj-\u003ekmod_btfs-\u003edata[i], NULL)) {\n+\t\t\tpr_warn(\"sec '%s': ignored duplicate module '%s'\\n\",\n+\t\t\t\tsec_name, obj-\u003ekmod_btfs-\u003edata[i]);\n+\t\t\tcontinue;\n+\t\t}\n+\t\terr = hashmap__set(obj-\u003ekmod_btfs-\u003ehashmap, obj-\u003ekmod_btfs-\u003edata[i],\n+\t\t\t\t   0, NULL, NULL);\n+\t\tif (err)\n+\t\t\tgoto err_out;\n+\t\tobj-\u003ekmod_btfs-\u003enr_names++;\n+\t}\n+\treturn 0;\n+\n+err_out:\n+\thashmap__free(obj-\u003ekmod_btfs-\u003ehashmap);\n+\tzfree(\u0026obj-\u003ekmod_btfs-\u003edata);\n+\tzfree(\u0026obj-\u003ekmod_btfs);\n+\treturn err;\n+}\n+\n static int\n bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,\n \t\t\t const char *sec_name, int sec_idx)\n@@ -4034,6 +4115,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)\n \t\t\t\tmemcpy(obj-\u003ejumptables_data, data-\u003ed_buf, data-\u003ed_size);\n \t\t\t\tobj-\u003ejumptables_data_sz = data-\u003ed_size;\n \t\t\t\tobj-\u003eefile.jumptables_data_shndx = idx;\n+\t\t\t} else if (strcmp(name, KMODS_BTFS_SEC) == 0) {\n+\t\t\t\terr = bpf_object__collect_kmod_btf_names(obj, data, name);\n+\t\t\t\tif (err)\n+\t\t\t\t\treturn err;\n \t\t\t} else {\n \t\t\t\tpr_info(\"elf: skipping unrecognized data section(%d) %s\\n\",\n \t\t\t\t\tidx, name);\n@@ -5803,6 +5888,11 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,\n \treturn 0;\n }\n \n+static bool is_kmod_btf_needed(struct bpf_object *obj, const char *name)\n+{\n+\treturn hashmap_find(obj-\u003ekmod_btfs-\u003ehashmap, (long)name, NULL);\n+}\n+\n static int load_module_btfs(struct bpf_object *obj)\n {\n \tstruct bpf_btf_info info;\n@@ -5867,6 +5957,12 @@ static int load_module_btfs(struct bpf_object *obj)\n \t\t\tcontinue;\n \t\t}\n \n+\t\tif (obj-\u003ekmod_btfs \u0026\u0026 obj-\u003ekmod_btfs-\u003ehashmap \u0026\u0026\n+\t\t    !is_kmod_btf_needed(obj, name)) {\n+\t\t\tclose(fd);\n+\t\t\tcontinue;\n+\t\t}\n+\n \t\tbtf = btf_get_from_fd(fd, obj-\u003ebtf_vmlinux);\n \t\terr = libbpf_get_error(btf);\n \t\tif (err) {\n@@ -5891,6 +5987,10 @@ static int load_module_btfs(struct bpf_object *obj)\n \t\t\tbreak;\n \t\t}\n \t\tobj-\u003ebtf_module_cnt++;\n+\n+\t\tif (obj-\u003ekmod_btfs \u0026\u0026\n+\t\t    obj-\u003ekmod_btfs-\u003enr_names == obj-\u003ekmod_btfs-\u003enr_loaded)\n+\t\t\tbreak;\n \t}\n \n \tif (err) {\n@@ -9030,6 +9130,12 @@ static void bpf_object_cleanup_btf(struct bpf_object *obj)\n \t/* clean up vmlinux BTF */\n \tbtf__free(obj-\u003ebtf_vmlinux);\n \tobj-\u003ebtf_vmlinux = NULL;\n+\n+\tif (obj-\u003ekmod_btfs) {\n+\t\thashmap__free(obj-\u003ekmod_btfs-\u003ehashmap);\n+\t\tzfree(\u0026obj-\u003ekmod_btfs-\u003edata);\n+\t\tzfree(\u0026obj-\u003ekmod_btfs);\n+\t}\n }\n \n static void bpf_object_post_load_cleanup(struct bpf_object *obj)\ndiff --git a/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c\nnew file mode 100644\nindex 0000000000000..d875fcce6681d\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c\n@@ -0,0 +1,52 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \u003ctest_progs.h\u003e\n+#include \"kmod_btfs.skel.h\"\n+#include \"kmod_btfs_nonexist.skel.h\"\n+#include \"kmod_btfs_mix.skel.h\"\n+\n+static void kmod_btfs_pass(void)\n+{\n+\tstruct kmod_btfs *kmod_btfs_skel;\n+\n+\tkmod_btfs_skel = kmod_btfs__open_and_load();\n+\tif (!ASSERT_OK_PTR(kmod_btfs_skel, \"kmod_btfs__open_and_load\"))\n+\t\treturn;\n+\n+\tkmod_btfs__destroy(kmod_btfs_skel);\n+}\n+\n+static void kmod_btfs_nonexist(void)\n+{\n+\tstruct kmod_btfs_nonexist *kmod_btfs_nonexist_skel;\n+\n+\tkmod_btfs_nonexist_skel = kmod_btfs_nonexist__open_and_load();\n+\tASSERT_NULL(kmod_btfs_nonexist_skel, \"kmod_btfs_nonexist__open_and_load\");\n+}\n+\n+static void kmod_btfs_mix(void)\n+{\n+\tstruct kmod_btfs_mix *kmod_btfs_mix_skel;\n+\n+\tkmod_btfs_mix_skel = kmod_btfs_mix__open_and_load();\n+\tif (!ASSERT_OK_PTR(kmod_btfs_mix_skel, \"kmod_btfs_mix__open_and_load\"))\n+\t\treturn;\n+\n+\tkmod_btfs_mix__destroy(kmod_btfs_mix_skel);\n+}\n+\n+void test_kmod_btfs(void)\n+{\n+\tif (!env.has_testmod) {\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\n+\tif (test__start_subtest(\"kmod_btfs_pass\"))\n+\t\tkmod_btfs_pass();\n+\n+\tif (test__start_subtest(\"kmod_btfs_nonexist\"))\n+\t\tkmod_btfs_nonexist();\n+\n+\tif (test__start_subtest(\"kmod_btfs_mix\"))\n+\t\tkmod_btfs_mix();\n+}\ndiff --git a/tools/testing/selftests/bpf/progs/kmod_btfs.c b/tools/testing/selftests/bpf/progs/kmod_btfs.c\nnew file mode 100644\nindex 0000000000000..8b986bc2e9ff7\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/kmod_btfs.c\n@@ -0,0 +1,14 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003cvmlinux.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+\n+SEC(\"fexit/bpf_testmod_loop_test\")\n+int test_kmod_btfs(void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+DEFINE_KMOD_BTFS(_needed_kmods) = { \"bpf_testmod\" };\n+\n+char _license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/kmod_btfs_mix.c b/tools/testing/selftests/bpf/progs/kmod_btfs_mix.c\nnew file mode 100644\nindex 0000000000000..f445cf56ab161\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/kmod_btfs_mix.c\n@@ -0,0 +1,15 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003cvmlinux.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+\n+SEC(\"fexit/bpf_testmod_loop_test\")\n+int test_kmod_btfs_mix(void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+/* mix of duplicated and unneeded modules */\n+DEFINE_KMOD_BTFS(_needed_kmods) = { \"bpf_testmod\", \"bpf_testmod\", \"bpf_test_no_cfi\" };\n+\n+char _license[] SEC(\"license\") = \"GPL\";\ndiff --git a/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c\nnew file mode 100644\nindex 0000000000000..1638361e57322\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c\n@@ -0,0 +1,18 @@\n+// SPDX-License-Identifier: GPL-2.0\n+\n+#include \u003cvmlinux.h\u003e\n+#include \u003cbpf/bpf_helpers.h\u003e\n+\n+SEC(\"fexit/bpf_testmod_loop_test\")\n+int test_kmod_btfs_nonexist(void *ctx)\n+{\n+\treturn 0;\n+}\n+\n+/*\n+ * This should fail to load, because .kmod_btfs does not contain\n+ * the needed module 'bpf_testmod'.\n+ */\n+DEFINE_KMOD_BTFS(_needed_kmods) = { \"module_nonexist\" };\n+\n+char _license[] SEC(\"license\") = \"GPL\";\n"
}

2/1 2026/08/13 04:19 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the userspace libbpf library (tools/lib/bpf) and adds selftests. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
  even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. List them in the EnableConfigs
   output array, and DO NOT add a '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:
For your convenience, here is the diff of the changes:
commit 6aa8f7601b07fdaa3fc744696acb256b09983592
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Aug 13 04:19:09 2026 +0000

    syz-cluster: applied patch under review

diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index 9d160b5b9c0ee..171ea055cd323 100644
--- a/tools/lib/bpf/bpf_helpers.h
+++ b/tools/lib/bpf/bpf_helpers.h
@@ -188,6 +188,20 @@ enum libbpf_tristate {
 	TRI_MODULE = 2,
 };
 
+/* Helper typedef for declaring kernel module names that need BTF loading.
+ *
+ * Usage: define an array in the ".kmod_btfs" ELF section to specify
+ * which modules need BTF loading:
+ *
+ *   DEFINE_KMOD_BTFS(_needed_kmods)= { "module1", "module2", ... };
+ *
+ * This avoids unnecessary BTF loading and speeds up the BPF program
+ * load process.
+ */
+#define KMOD_NAME_LEN 64
+#define DEFINE_KMOD_BTFS(name) \
+	SEC(".kmod_btfs") char name[][KMOD_NAME_LEN]
+
 #define __kconfig __attribute__((section(".kconfig")))
 #define __ksym __attribute__((section(".ksyms")))
 #define __kptr_untrusted __attribute__((btf_type_tag("kptr_untrusted")))
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index 514e4e9daa825..9fe8a653e8b6b 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -548,6 +548,7 @@ struct bpf_struct_ops {
 #define STRUCT_OPS_SEC ".struct_ops"
 #define STRUCT_OPS_LINK_SEC ".struct_ops.link"
 #define ARENA_SEC ".addr_space.1"
+#define KMODS_BTFS_SEC ".kmod_btfs"
 
 enum libbpf_map_type {
 	LIBBPF_MAP_UNSPEC,
@@ -703,6 +704,9 @@ enum bpf_object_state {
 	OBJ_LOADED,
 };
 
+/* Should match the definition in bpf_helpers.h */
+#define KMOD_NAME_LEN 64
+
 struct bpf_object {
 	char name[BPF_OBJ_NAME_LEN];
 	char license[64];
@@ -779,6 +783,14 @@ struct bpf_object {
 	char *token_path;
 	int token_fd;
 
+	/* kernel module BTFs to load, declared in ".kmod_btfs" ELF section */
+	struct {
+		char (*data)[KMOD_NAME_LEN];
+		size_t nr_names;
+		size_t nr_loaded;
+		struct hashmap *hashmap;
+	} *kmod_btfs;
+
 	char path[];
 };
 
@@ -901,6 +913,75 @@ bpf_object__init_prog(struct bpf_object *obj, struct bpf_program *prog,
 	return -ENOMEM;
 }
 
+static size_t mod_name_hash_fn(long key, void *ctx)
+{
+	return str_hash((char *)key);
+}
+
+static bool mod_name_equal_fn(long key1, long key2, void *ctx)
+{
+	return strcmp((char *)key1, (char *)key2) == 0;
+}
+
+static int
+bpf_object__collect_kmod_btf_names(struct bpf_object *obj, Elf_Data *sec_data,
+				   const char *sec_name)
+{
+	int module_cnt, i, err = 0;
+
+	if (obj->kmod_btfs) {
+		pr_warn("sec '%s': duplicate sections detected\n", sec_name);
+		return -EEXIST;
+	}
+
+	if (sec_data->d_size % KMOD_NAME_LEN != 0) {
+		pr_warn("sec '%s': size %zu should be multiple of %d\n",
+			sec_name, sec_data->d_size, KMOD_NAME_LEN);
+		return -EINVAL;
+	}
+
+	module_cnt = sec_data->d_size / KMOD_NAME_LEN;
+	obj->kmod_btfs = calloc(1, sizeof(*obj->kmod_btfs));
+	if (!obj->kmod_btfs)
+		return -ENOMEM;
+
+	obj->kmod_btfs->data = calloc(module_cnt, KMOD_NAME_LEN);
+	if (!obj->kmod_btfs->data) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+	memcpy(obj->kmod_btfs->data, sec_data->d_buf, sec_data->d_size);
+
+	obj->kmod_btfs->hashmap = hashmap__new(mod_name_hash_fn,
+					       mod_name_equal_fn, NULL);
+	if (IS_ERR(obj->kmod_btfs->hashmap)) {
+		err = PTR_ERR(obj->kmod_btfs->hashmap);
+		goto err_out;
+	}
+
+	for (i = 0; i < module_cnt; i++) {
+		obj->kmod_btfs->data[i][KMOD_NAME_LEN - 1] = '\0';
+		if (hashmap__find(obj->kmod_btfs->hashmap,
+				  obj->kmod_btfs->data[i], NULL)) {
+			pr_warn("sec '%s': ignored duplicate module '%s'\n",
+				sec_name, obj->kmod_btfs->data[i]);
+			continue;
+		}
+		err = hashmap__set(obj->kmod_btfs->hashmap, obj->kmod_btfs->data[i],
+				   0, NULL, NULL);
+		if (err)
+			goto err_out;
+		obj->kmod_btfs->nr_names++;
+	}
+	return 0;
+
+err_out:
+	hashmap__free(obj->kmod_btfs->hashmap);
+	zfree(&obj->kmod_btfs->data);
+	zfree(&obj->kmod_btfs);
+	return err;
+}
+
 static int
 bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
 			 const char *sec_name, int sec_idx)
@@ -4034,6 +4115,10 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 				memcpy(obj->jumptables_data, data->d_buf, data->d_size);
 				obj->jumptables_data_sz = data->d_size;
 				obj->efile.jumptables_data_shndx = idx;
+			} else if (strcmp(name, KMODS_BTFS_SEC) == 0) {
+				err = bpf_object__collect_kmod_btf_names(obj, data, name);
+				if (err)
+					return err;
 			} else {
 				pr_info("elf: skipping unrecognized data section(%d) %s\n",
 					idx, name);
@@ -5803,6 +5888,11 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
 	return 0;
 }
 
+static bool is_kmod_btf_needed(struct bpf_object *obj, const char *name)
+{
+	return hashmap_find(obj->kmod_btfs->hashmap, (long)name, NULL);
+}
+
 static int load_module_btfs(struct bpf_object *obj)
 {
 	struct bpf_btf_info info;
@@ -5867,6 +5957,12 @@ static int load_module_btfs(struct bpf_object *obj)
 			continue;
 		}
 
+		if (obj->kmod_btfs && obj->kmod_btfs->hashmap &&
+		    !is_kmod_btf_needed(obj, name)) {
+			close(fd);
+			continue;
+		}
+
 		btf = btf_get_from_fd(fd, obj->btf_vmlinux);
 		err = libbpf_get_error(btf);
 		if (err) {
@@ -5891,6 +5987,10 @@ static int load_module_btfs(struct bpf_object *obj)
 			break;
 		}
 		obj->btf_module_cnt++;
+
+		if (obj->kmod_btfs &&
+		    obj->kmod_btfs->nr_names == obj->kmod_btfs->nr_loaded)
+			break;
 	}
 
 	if (err) {
@@ -9030,6 +9130,12 @@ static void bpf_object_cleanup_btf(struct bpf_object *obj)
 	/* clean up vmlinux BTF */
 	btf__free(obj->btf_vmlinux);
 	obj->btf_vmlinux = NULL;
+
+	if (obj->kmod_btfs) {
+		hashmap__free(obj->kmod_btfs->hashmap);
+		zfree(&obj->kmod_btfs->data);
+		zfree(&obj->kmod_btfs);
+	}
 }
 
 static void bpf_object_post_load_cleanup(struct bpf_object *obj)
diff --git a/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
new file mode 100644
index 0000000000000..d875fcce6681d
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include "kmod_btfs.skel.h"
+#include "kmod_btfs_nonexist.skel.h"
+#include "kmod_btfs_mix.skel.h"
+
+static void kmod_btfs_pass(void)
+{
+	struct kmod_btfs *kmod_btfs_skel;
+
+	kmod_btfs_skel = kmod_btfs__open_and_load();
+	if (!ASSERT_OK_PTR(kmod_btfs_skel, "kmod_btfs__open_and_load"))
+		return;
+
+	kmod_btfs__destroy(kmod_btfs_skel);
+}
+
+static void kmod_btfs_nonexist(void)
+{
+	struct kmod_btfs_nonexist *kmod_btfs_nonexist_skel;
+
+	kmod_btfs_nonexist_skel = kmod_btfs_nonexist__open_and_load();
+	ASSERT_NULL(kmod_btfs_nonexist_skel, "kmod_btfs_nonexist__open_and_load");
+}
+
+static void kmod_btfs_mix(void)
+{
+	struct kmod_btfs_mix *kmod_btfs_mix_skel;
+
+	kmod_btfs_mix_skel = kmod_btfs_mix__open_and_load();
+	if (!ASSERT_OK_PTR(kmod_btfs_mix_skel, "kmod_btfs_mix__open_and_load"))
+		return;
+
+	kmod_btfs_mix__destroy(kmod_btfs_mix_skel);
+}
+
+void test_kmod_btfs(void)
+{
+	if (!env.has_testmod) {
+		test__skip();
+		return;
+	}
+
+	if (test__start_subtest("kmod_btfs_pass"))
+		kmod_btfs_pass();
+
+	if (test__start_subtest("kmod_btfs_nonexist"))
+		kmod_btfs_nonexist();
+
+	if (test__start_subtest("kmod_btfs_mix"))
+		kmod_btfs_mix();
+}
diff --git a/tools/testing/selftests/bpf/progs/kmod_btfs.c b/tools/testing/selftests/bpf/progs/kmod_btfs.c
new file mode 100644
index 0000000000000..8b986bc2e9ff7
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kmod_btfs.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("fexit/bpf_testmod_loop_test")
+int test_kmod_btfs(void *ctx)
+{
+	return 0;
+}
+
+DEFINE_KMOD_BTFS(_needed_kmods) = { "bpf_testmod" };
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/kmod_btfs_mix.c b/tools/testing/selftests/bpf/progs/kmod_btfs_mix.c
new file mode 100644
index 0000000000000..f445cf56ab161
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kmod_btfs_mix.c
@@ -0,0 +1,15 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("fexit/bpf_testmod_loop_test")
+int test_kmod_btfs_mix(void *ctx)
+{
+	return 0;
+}
+
+/* mix of duplicated and unneeded modules */
+DEFINE_KMOD_BTFS(_needed_kmods) = { "bpf_testmod", "bpf_testmod", "bpf_test_no_cfi" };
+
+char _license[] SEC("license") = "GPL";
diff --git a/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
new file mode 100644
index 0000000000000..1638361e57322
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
@@ -0,0 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+
+SEC("fexit/bpf_testmod_loop_test")
+int test_kmod_btfs_nonexist(void *ctx)
+{
+	return 0;
+}
+
+/*
+ * This should fail to load, because .kmod_btfs does not contain
+ * the needed module 'bpf_testmod'.
+ */
+DEFINE_KMOD_BTFS(_needed_kmods) = { "module_nonexist" };
+
+char _license[] SEC("license") = "GPL";

3/2 2026/08/13 04:19 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5916
							output: 69
							thoughts: 532
						

Thoughts:
**WorthFuzzing Analysis**

Okay, so this patch is touching `tools/lib/bpf/libbpf.c` and `tools/lib/bpf/bpf_helpers.h`, plus some selftests. Seems like it's introducing a new feature to libbpf related to loading BTF for kernel modules via a `.kmod_btfs` ELF section. My initial thought is userspace, libbpf changes. But I always double-check.

Let me verify... okay, I see the diff shows changes only in `tools/lib/bpf/` and `tools/testing/selftests/bpf/`. Specifically, I am seeing updates to: `tools/lib/bpf/bpf_helpers.h`, `tools/lib/bpf/libbpf.c`, `tools/testing/selftests/bpf/prog_tests/kmod_btfs.c`, `tools/testing/selftests/bpf/progs/kmod_btfs.c`, `tools/testing/selftests/bpf/progs/kmod_btfs_mix.c`, and `tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c`. No kernel code changes here at all. We are good.

Since `tools/` is userspace code – `libbpf` is a userspace library – this *definitely* doesn't require kernel fuzzing. The patch doesn't introduce any new kernel functionality that a tool like syzkaller would need to examine. Even if syzkaller *did* fuzz `libbpf` (which it doesn't, it uses the syscall), it's still a userspace change.

Alright, just to be absolutely sure, let's double-check again... Nope, nothing. Only changes in `tools/lib/bpf/` and the `selftests`.

Therefore, I'm comfortable saying `WorthFuzzing: false`. The patch is solely within the user space components of `libbpf` and doesn't affect the kernel's functional code.

I will set the results, indicating `WorthFuzzing: false`.



4/2 2026/08/13 04:19 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the userspace libbpf library (tools/lib/bpf) and adds selftests. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only modifies the userspace libbpf library (tools/lib/bpf) and adds selftests. It does not make any functional changes to the kernel code itself.",
  "WorthFuzzing": false
}

5/1 2026/08/13 04:19 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)