BPF List
 help / color / mirror / Atom feed
* [RFC PATCH bpf-next 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs
@ 2026-08-06  4:20 Fuyu Zhao
  2026-08-06  4:20 ` [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section Fuyu Zhao
  2026-08-06  4:20 ` [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading Fuyu Zhao
  0 siblings, 2 replies; 7+ messages in thread
From: Fuyu Zhao @ 2026-08-06  4:20 UTC (permalink / raw)
  To: bpf; +Cc: Fuyu Zhao

Currently, during BPF object loading, load_module_btfs() unconditionally
iterates over all loaded kernel modules and loads their BTFs. This
introduces unnecessary overhead when a BPF program only needs a specific,
small subset of modules. In environments with hundreds of modules,
loading all module measurably increases the loading time.

This series introduces a ".kmod_btfs" ELF section, allowing BPF programs
to declare the modules that need BTF loading. libbpf parses this
section, collects the module names, and skips loading BTFs for any
module not in the list, thereby reducing overhead. It also stops the
iteration early once all declared modules are found.

Usage example:

  DEFINE_KMOD_BTFS(_needed_kmods) = { "bpf_testmod" };

Performance impact (<skel>__open_and_load() time):

  Modules loaded | Without .kmod_btfs | With .kmod_btfs | Speedup
  ---------------|--------------------|-----------------|--------
  1              | 35.0 ms            | 35.0 ms         | Baseline
  10             | 36.5 ms            | 35.1 ms         | +3.8%
  100            | 46.2 ms            | 35.9 ms         | +22.3%
  300            | 65.2 ms            | 38.0 ms         | +41.7%

Fuyu Zhao (2):
  libbpf: support selective kernel module BTF loading via .kmod_btfs
    section
  selftests/bpf: add tests for selective kmod BTF loading

 tools/lib/bpf/bpf_helpers.h                   |  14 +++
 tools/lib/bpf/libbpf.c                        | 112 ++++++++++++++++++
 .../selftests/bpf/prog_tests/kmod_btfs.c      |  47 ++++++++
 tools/testing/selftests/bpf/progs/kmod_btfs.c |  14 +++
 .../selftests/bpf/progs/kmod_btfs_mix.c       |  15 +++
 .../selftests/bpf/progs/kmod_btfs_nonexist.c  |  17 +++
 6 files changed, 219 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
 create mode 100644 tools/testing/selftests/bpf/progs/kmod_btfs.c
 create mode 100644 tools/testing/selftests/bpf/progs/kmod_btfs_mix.c
 create mode 100644 tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c

-- 
2.34.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section
  2026-08-06  4:20 [RFC PATCH bpf-next 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs Fuyu Zhao
@ 2026-08-06  4:20 ` Fuyu Zhao
  2026-08-06  4:33   ` sashiko-bot
  2026-08-06  4:20 ` [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading Fuyu Zhao
  1 sibling, 1 reply; 7+ messages in thread
From: Fuyu Zhao @ 2026-08-06  4:20 UTC (permalink / raw)
  To: bpf
  Cc: Fuyu Zhao, Andrii Nakryiko, Eduard Zingerman, Ihor Solodrai,
	Alexei Starovoitov, Daniel Borkmann, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, open list

Add support for a new ELF section ".kmod_btfs" that allows BPF programs
to declare which kernel modules need BTF loading. This avoids loading
all module BTFs and speeds up program load when only a subset of modules
is needed.

Specifically, this patch introduces two internal functions:
  - bpf_object__collect_kmod_btf_names(): parses the ".kmod_btfs"
    section and collects the declared module names.
  - is_kmod_btf_needed(): determines whether a given module's BTF
    should be loaded, allowing libbpf to skip unneeded modules.

Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com>
---
 tools/lib/bpf/bpf_helpers.h |  14 +++++
 tools/lib/bpf/libbpf.c      | 112 ++++++++++++++++++++++++++++++++++++
 2 files changed, 126 insertions(+)

diff --git a/tools/lib/bpf/bpf_helpers.h b/tools/lib/bpf/bpf_helpers.h
index 9d160b5b9c0e..171ea055cd32 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 514e4e9daa82..5cefe82e4a67 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,12 @@ enum bpf_object_state {
 	OBJ_LOADED,
 };
 
+/* Should match typedef in bpf_helpers.h */
+#define KMOD_NAME_LEN 64
+
+#define KMODS_BTF_UNLOADED 0UL
+#define KMODS_BTF_LOADED   1UL
+
 struct bpf_object {
 	char name[BPF_OBJ_NAME_LEN];
 	char license[64];
@@ -779,6 +786,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 +916,68 @@ 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 (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)
+		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],
+				   KMODS_BTF_UNLOADED, 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 +4111,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 +5884,21 @@ 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)
+{
+	uintptr_t val;
+
+	if (!hashmap__find(obj->kmod_btfs->hashmap, name, &val))
+		return false;
+
+	if (val == KMODS_BTF_LOADED)
+		return false;
+
+	hashmap__set(obj->kmod_btfs->hashmap, name, KMODS_BTF_LOADED, NULL, NULL);
+	obj->kmod_btfs->nr_loaded++;
+	return true;
+}
+
 static int load_module_btfs(struct bpf_object *obj)
 {
 	struct bpf_btf_info info;
@@ -5867,6 +5963,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 +5993,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 +9136,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)
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading
  2026-08-06  4:20 [RFC PATCH bpf-next 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs Fuyu Zhao
  2026-08-06  4:20 ` [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section Fuyu Zhao
@ 2026-08-06  4:20 ` Fuyu Zhao
  2026-08-06  4:29   ` sashiko-bot
  1 sibling, 1 reply; 7+ messages in thread
From: Fuyu Zhao @ 2026-08-06  4:20 UTC (permalink / raw)
  To: bpf
  Cc: Fuyu Zhao, Andrii Nakryiko, Eduard Zingerman, Ihor Solodrai,
	Alexei Starovoitov, Daniel Borkmann, Kumar Kartikeya Dwivedi,
	Martin KaFai Lau, Song Liu, Yonghong Song, Jiri Olsa,
	Emil Tsalapatis, Shuah Khan, open list,
	open list:KERNEL SELFTEST FRAMEWORK

Add test cases to verify the new ".kmod_btfs" section logic in libbpf.
The added test cases cover three main scenarios:

  - Valid module: targeting an existing kernel module BTF to ensure
    successful loading.
  - Non-existent module: targeting a fake or missing module, verifying
    that the process results in an expected failure.
  - Duplicates and unneeded: providing a mix of repeated and extra
    module names to ensure the parsing logic remains robust.

Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com>
---
 .../selftests/bpf/prog_tests/kmod_btfs.c      | 47 +++++++++++++++++++
 tools/testing/selftests/bpf/progs/kmod_btfs.c | 14 ++++++
 .../selftests/bpf/progs/kmod_btfs_mix.c       | 15 ++++++
 .../selftests/bpf/progs/kmod_btfs_nonexist.c  | 17 +++++++
 4 files changed, 93 insertions(+)
 create mode 100644 tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
 create mode 100644 tools/testing/selftests/bpf/progs/kmod_btfs.c
 create mode 100644 tools/testing/selftests/bpf/progs/kmod_btfs_mix.c
 create mode 100644 tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c

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 000000000000..0608f2809223
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
@@ -0,0 +1,47 @@
+// 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 (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 000000000000..8b986bc2e9ff
--- /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 000000000000..f445cf56ab16
--- /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 000000000000..431e3273821b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
@@ -0,0 +1,17 @@
+// 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";
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading
  2026-08-06  4:20 ` [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading Fuyu Zhao
@ 2026-08-06  4:29   ` sashiko-bot
  2026-08-06  8:39     ` Fuyu Zhao
  0 siblings, 1 reply; 7+ messages in thread
From: sashiko-bot @ 2026-08-06  4:29 UTC (permalink / raw)
  To: Fuyu Zhao; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The test `test_kmod_btfs` depends on the `bpf_testmod` kernel module being available, but does not check `env.has_testmod` before running.
- [Low] Multi-line comment does not follow BPF subsystem style guidelines.
--

--- Patch [2]: [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading ---
commit 35262af12350c47550b117d06e881e0de422cb5d
Author: Fuyu Zhao <zhaofuyu@vivo.com>

selftests/bpf: add tests for selective kmod BTF loading

This commit adds test cases to verify the new .kmod_btfs section logic in
libbpf. It covers scenarios with valid, non-existent, and duplicated module
BTF targets to ensure robust loading and parsing.

> diff --git a/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
[ ... ]
> +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 (test__start_subtest("kmod_btfs_pass"))

[Severity: Medium]
Since this test depends on bpf_testmod being available, should we check
env.has_testmod at the start of the test and skip it if not found?

Without it, running the selftests on a system where the test module is not
built or fails to load might cause erroneous test failures instead of being
gracefully skipped.

> +		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_nonexist.c b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
[ ... ]
> +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'.
> + */

[Severity: Low]
This isn't a bug, but does this multi-line comment follow the BPF
subsystem style guidelines?

The BPF guidelines usually require the opening /* to be on its own line.

> +DEFINE_KMOD_BTFS(_needed_kmods) = { "module_nonexist" };
> +
> +char _license[] SEC("license") = "GPL";

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260806042042.3239428-1-zhaofuyu@vivo.com?part=2

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section
  2026-08-06  4:20 ` [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section Fuyu Zhao
@ 2026-08-06  4:33   ` sashiko-bot
  2026-08-06  8:38     ` Fuyu Zhao
  0 siblings, 1 reply; 7+ messages in thread
From: sashiko-bot @ 2026-08-06  4:33 UTC (permalink / raw)
  To: Fuyu Zhao; +Cc: bpf

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] Transient stack pointer overwrites persistent hashmap key in is_kmod_btf_needed(), breaking hashmap integrity on bucket collisions.
- [Medium] Memory allocation failure for `obj->kmod_btfs->data` silently returns success (0), masking the error and corrupting intended behavior.
- [Medium] Processing multiple `.kmod_btfs` sections overwrites previous allocations without freeing them.
--

--- Patch [1]: [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section ---
commit 5306ac18160e3a8c2a0f3dd2eda202169f998df6
Author: Fuyu Zhao <zhaofuyu@vivo.com>

libbpf: support selective kernel module BTF loading via .kmod_btfs section

Add support for a new ELF section ".kmod_btfs" that allows BPF programs
to declare which kernel modules need BTF loading. This avoids loading
all module BTFs and speeds up program load when only a subset of modules
is needed.

> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 514e4e9daa825..5cefe82e4a679 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c

[ ... ]

> @@ -901,6 +916,68 @@ 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 (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));

[Severity: Medium]
If a crafted ELF file contains multiple .kmod_btfs sections, could this calloc
overwrite a previously allocated obj->kmod_btfs and cause a memory leak of
the hashmap and data arrays?

> +	if (!obj->kmod_btfs)
> +		return -ENOMEM;
> +
> +	obj->kmod_btfs->data = calloc(module_cnt, KMOD_NAME_LEN);
> +	if (!obj->kmod_btfs->data)
> +		goto err_out;

[Severity: Medium]
Does this error path silently return success?

Because err is initialized to 0, this goto err_out will return 0 instead of
-ENOMEM. This causes obj->kmod_btfs to be freed in the cleanup path while
the function signals success, making load_module_btfs() fall back to loading
all module BTFs.

> +	memcpy(obj->kmod_btfs->data, sec_data->d_buf, sec_data->d_size);

[ ... ]

> @@ -5803,6 +5884,21 @@ 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)
> +{
> +	uintptr_t val;
> +
> +	if (!hashmap__find(obj->kmod_btfs->hashmap, name, &val))
> +		return false;
> +
> +	if (val == KMODS_BTF_LOADED)
> +		return false;
> +
> +	hashmap__set(obj->kmod_btfs->hashmap, name, KMODS_BTF_LOADED, NULL, NULL);

[Severity: Medium]
Can this hashmap__set() corrupt the hashmap by storing a transient stack
pointer as the key?

Looking at load_module_btfs(), name is a local stack array passed to
is_kmod_btf_needed() on every iteration:

load_module_btfs() {
    char name[64];
    ...
        if (obj->kmod_btfs && obj->kmod_btfs->hashmap &&
            !is_kmod_btf_needed(obj, name)) {
    ...
}

Since hashmap__set() only stores the pointer address, all entries will
share the same pointer to the local name array. If a bucket collision
occurs, strcmp(key1, key2) will evaluate to 0 since both point to the
same stack address, falsely matching existing entries and skipping the
requested module.

> +	obj->kmod_btfs->nr_loaded++;
> +	return true;
> +}
> +

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260806042042.3239428-1-zhaofuyu@vivo.com?part=1

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section
  2026-08-06  4:33   ` sashiko-bot
@ 2026-08-06  8:38     ` Fuyu Zhao
  0 siblings, 0 replies; 7+ messages in thread
From: Fuyu Zhao @ 2026-08-06  8:38 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf



On 8/6/2026 12:33 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
> - [Medium] Transient stack pointer overwrites persistent hashmap key in is_kmod_btf_needed(), breaking hashmap integrity on bucket collisions.
> - [Medium] Memory allocation failure for `obj->kmod_btfs->data` silently returns success (0), masking the error and corrupting intended behavior.
> - [Medium] Processing multiple `.kmod_btfs` sections overwrites previous allocations without freeing them.
> --
> 
> --- Patch [1]: [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section ---
> commit 5306ac18160e3a8c2a0f3dd2eda202169f998df6
> Author: Fuyu Zhao <zhaofuyu@vivo.com>
> 
> libbpf: support selective kernel module BTF loading via .kmod_btfs section
> 
> Add support for a new ELF section ".kmod_btfs" that allows BPF programs
> to declare which kernel modules need BTF loading. This avoids loading
> all module BTFs and speeds up program load when only a subset of modules
> is needed.
> 
>> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
>> index 514e4e9daa825..5cefe82e4a679 100644
>> --- a/tools/lib/bpf/libbpf.c
>> +++ b/tools/lib/bpf/libbpf.c
> 
> [ ... ]
> 
>> @@ -901,6 +916,68 @@ 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 (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));
> 
> [Severity: Medium]
> If a crafted ELF file contains multiple .kmod_btfs sections, could this calloc
> overwrite a previously allocated obj->kmod_btfs and cause a memory leak of
> the hashmap and data arrays?
> 

Good catch, thanks! I missed the sanity check for duplicate sections.
I will add a check `if (obj->kmod_btfs) return -EEXIST;` to prevent duplicate
section parsing and fix this potential memory leak in v2.

>> +	if (!obj->kmod_btfs)
>> +		return -ENOMEM;
>> +
>> +	obj->kmod_btfs->data = calloc(module_cnt, KMOD_NAME_LEN);
>> +	if (!obj->kmod_btfs->data)
>> +		goto err_out;
> 
> [Severity: Medium]
> Does this error path silently return success?
> 
> Because err is initialized to 0, this goto err_out will return 0 instead of
> -ENOMEM. This causes obj->kmod_btfs to be freed in the cleanup path while
> the function signals success, making load_module_btfs() fall back to loading
> all module BTFs.
> 

My apologies for this careless oversight in error handling. Leaving `err` as 0
here indeed creates a dangerous silent error.

I will assign `err = -ENOMEM;` right before jumping to `err_out` and thoroughly
re-check every error path in the patch series for v2. Thanks again

>> +	memcpy(obj->kmod_btfs->data, sec_data->d_buf, sec_data->d_size);
> 
> [ ... ]
> 
>> @@ -5803,6 +5884,21 @@ 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)
>> +{
>> +	uintptr_t val;
>> +
>> +	if (!hashmap__find(obj->kmod_btfs->hashmap, name, &val))
>> +		return false;
>> +
>> +	if (val == KMODS_BTF_LOADED)
>> +		return false;
>> +
>> +	hashmap__set(obj->kmod_btfs->hashmap, name, KMODS_BTF_LOADED, NULL, NULL);
> 
> [Severity: Medium]
> Can this hashmap__set() corrupt the hashmap by storing a transient stack
> pointer as the key?
> 
> Looking at load_module_btfs(), name is a local stack array passed to
> is_kmod_btf_needed() on every iteration:
> 
> load_module_btfs() {
>     char name[64];
>     ...
>         if (obj->kmod_btfs && obj->kmod_btfs->hashmap &&
>             !is_kmod_btf_needed(obj, name)) {
>     ...
> }
> 
> Since hashmap__set() only stores the pointer address, all entries will
> share the same pointer to the local name array. If a bucket collision
> occurs, strcmp(key1, key2) will evaluate to 0 since both point to the
> same stack address, falsely matching existing entries and skipping the
> requested module.
> 

This is a valid point regarding pointer lifetime.

In v2, I will update `is_kmod_btf_needed()` to store persistent string
pointers from the pre-parsed ELF section instead of the stack buffer.

Thanks for pointing this out.

>> +	obj->kmod_btfs->nr_loaded++;
>> +	return true;
>> +}
>> +
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading
  2026-08-06  4:29   ` sashiko-bot
@ 2026-08-06  8:39     ` Fuyu Zhao
  0 siblings, 0 replies; 7+ messages in thread
From: Fuyu Zhao @ 2026-08-06  8:39 UTC (permalink / raw)
  To: sashiko-reviews; +Cc: bpf



On 8/6/2026 12:29 PM, sashiko-bot@kernel.org wrote:
> Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
> - [Medium] The test `test_kmod_btfs` depends on the `bpf_testmod` kernel module being available, but does not check `env.has_testmod` before running.
> - [Low] Multi-line comment does not follow BPF subsystem style guidelines.
> --
> 
> --- Patch [2]: [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading ---
> commit 35262af12350c47550b117d06e881e0de422cb5d
> Author: Fuyu Zhao <zhaofuyu@vivo.com>
> 
> selftests/bpf: add tests for selective kmod BTF loading
> 
> This commit adds test cases to verify the new .kmod_btfs section logic in
> libbpf. It covers scenarios with valid, non-existent, and duplicated module
> BTF targets to ensure robust loading and parsing.
> 
>> diff --git a/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
>> --- /dev/null
>> +++ b/tools/testing/selftests/bpf/prog_tests/kmod_btfs.c
> [ ... ]
>> +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 (test__start_subtest("kmod_btfs_pass"))
> 
> [Severity: Medium]
> Since this test depends on bpf_testmod being available, should we check
> env.has_testmod at the start of the test and skip it if not found?
> 
> Without it, running the selftests on a system where the test module is not
> built or fails to load might cause erroneous test failures instead of being
> gracefully skipped.
> 

Good point. I will add an `if (!env.has_testmod)` check in v2 to gracefully
skip the test when `bpf_testmod` is unavailable.

Thanks for the feedback.

>> +		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_nonexist.c b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
>> --- /dev/null
>> +++ b/tools/testing/selftests/bpf/progs/kmod_btfs_nonexist.c
> [ ... ]
>> +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'.
>> + */
> 
> [Severity: Low]
> This isn't a bug, but does this multi-line comment follow the BPF
> subsystem style guidelines?
> 
> The BPF guidelines usually require the opening /* to be on its own line.
> 

Thanks for the feedback. I will update the comment style in v2.

>> +DEFINE_KMOD_BTFS(_needed_kmods) = { "module_nonexist" };
>> +
>> +char _license[] SEC("license") = "GPL";
> 


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-08-06  8:39 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-06  4:20 [RFC PATCH bpf-next 0/2] libbpf: Improve BPF load performance by selectively loading kmod BTFs Fuyu Zhao
2026-08-06  4:20 ` [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section Fuyu Zhao
2026-08-06  4:33   ` sashiko-bot
2026-08-06  8:38     ` Fuyu Zhao
2026-08-06  4:20 ` [RFC PATCH bpf-next 2/2] selftests/bpf: add tests for selective kmod BTF loading Fuyu Zhao
2026-08-06  4:29   ` sashiko-bot
2026-08-06  8:39     ` Fuyu Zhao

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox