* [PATCH bpf-next v9 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
2026-09-15 12:41 [PATCH bpf-next v9 0/2] libbpf: improve BPF load performance by selectively loading module BTFs Fuyu Zhao
@ 2026-09-15 12:41 ` Fuyu Zhao
2026-09-15 13:33 ` bot+bpf-ci
2026-09-15 12:41 ` [PATCH bpf-next v9 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
2026-09-21 16:40 ` [PATCH bpf-next v9 0/2] libbpf: improve BPF load performance by selectively loading module BTFs patchwork-bot+netdevbpf
2 siblings, 1 reply; 6+ messages in thread
From: Fuyu Zhao @ 2026-09-15 12:41 UTC (permalink / raw)
To: bpf
Cc: eddyz87, andrii.nakryiko, alan.maguire, olsajiri, Fuyu Zhao,
Andrii Nakryiko
Add btf_module_allowlist and btf_module_allowlist_cnt fields to
bpf_object_open_opts to limit which kernel module BTFs libbpf is
allowed to load.
When the option is not specified, the existing behavior remains
unchanged. An explicitly specified empty allowlist prevents libbpf from
consulting any kernel module BTFs.
The allowlist limits which kernel module BTFs libbpf will consult
wherever module BTF might be needed.
Suggested-by: Andrii Nakryiko <andrii@kernel.org>
Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com>
---
tools/lib/bpf/libbpf.c | 70 ++++++++++++++++++++++++++++++++++++++++++
tools/lib/bpf/libbpf.h | 16 +++++++++-
2 files changed, 85 insertions(+), 1 deletion(-)
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
index c036e8a91ed8..a77115e1b171 100644
--- a/tools/lib/bpf/libbpf.c
+++ b/tools/lib/bpf/libbpf.c
@@ -751,6 +751,8 @@ struct bpf_object {
bool btf_modules_loaded;
size_t btf_module_cnt;
size_t btf_module_cap;
+ char **btf_module_allowlist;
+ ssize_t btf_module_allowlist_cnt;
/* optional log settings passed to BPF_BTF_LOAD and BPF_PROG_LOAD commands */
char *log_buf;
@@ -5851,6 +5853,21 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
return 0;
}
+static bool is_btf_mod_allowed(const struct bpf_object *obj, const char *name)
+{
+ ssize_t i;
+
+ if (obj->btf_module_allowlist_cnt < 0)
+ return true;
+
+ for (i = 0; i < obj->btf_module_allowlist_cnt; i++) {
+ if (strcmp(obj->btf_module_allowlist[i], name) == 0)
+ return true;
+ }
+
+ return false;
+}
+
static int load_module_btfs(struct bpf_object *obj)
{
struct bpf_btf_info info;
@@ -5873,6 +5890,9 @@ static int load_module_btfs(struct bpf_object *obj)
if (!kernel_supports(obj, FEAT_MODULE_BTF))
return 0;
+ if (obj->btf_module_allowlist_cnt == 0)
+ return 0;
+
while (true) {
err = bpf_btf_get_next_id(id, &id);
if (err && errno == ENOENT)
@@ -5915,6 +5935,11 @@ static int load_module_btfs(struct bpf_object *obj)
continue;
}
+ if (!is_btf_mod_allowed(obj, name)) {
+ close(fd);
+ continue;
+ }
+
btf = btf_get_from_fd(fd, obj->btf_vmlinux);
err = libbpf_get_error(btf);
if (err) {
@@ -5939,6 +5964,9 @@ static int load_module_btfs(struct bpf_object *obj)
break;
}
obj->btf_module_cnt++;
+
+ if (obj->btf_module_allowlist_cnt == obj->btf_module_cnt)
+ break;
}
if (err) {
@@ -8481,6 +8509,8 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf,
const struct bpf_object_open_opts *opts)
{
const char *kconfig, *btf_tmp_path, *token_path;
+ size_t mod_allow_cnt, i, j;
+ const char **mod_allow;
struct bpf_object *obj;
int err;
char *log_buf;
@@ -8525,6 +8555,22 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf,
if (token_path && strlen(token_path) >= PATH_MAX)
return ERR_PTR(-ENAMETOOLONG);
+ mod_allow = OPTS_GET(opts, btf_module_allowlist, NULL);
+ mod_allow_cnt = OPTS_GET(opts, btf_module_allowlist_cnt, 0);
+
+ if (mod_allow_cnt > SSIZE_MAX || (!mod_allow && mod_allow_cnt > 0))
+ return ERR_PTR(-EINVAL);
+
+ for (i = 0; i < mod_allow_cnt; i++) {
+ if (!mod_allow[i] || !mod_allow[i][0])
+ return ERR_PTR(-EINVAL);
+
+ for (j = 0; j < i; j++) {
+ if (strcmp(mod_allow[i], mod_allow[j]) == 0)
+ return ERR_PTR(-EINVAL);
+ }
+ }
+
obj = bpf_object__new(path, obj_buf, obj_buf_sz, obj_name);
if (IS_ERR(obj))
return obj;
@@ -8563,6 +8609,24 @@ static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf,
}
}
+ obj->btf_module_allowlist_cnt = mod_allow ? mod_allow_cnt : -1;
+ if (mod_allow_cnt > 0) {
+ obj->btf_module_allowlist =
+ calloc(mod_allow_cnt, sizeof(*obj->btf_module_allowlist));
+ if (!obj->btf_module_allowlist) {
+ err = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < mod_allow_cnt; i++) {
+ obj->btf_module_allowlist[i] = strdup(mod_allow[i]);
+ if (!obj->btf_module_allowlist[i]) {
+ err = -ENOMEM;
+ goto out;
+ }
+ }
+ }
+
err = bpf_object__elf_init(obj);
err = err ? : bpf_object__elf_collect(obj);
err = err ? : bpf_object__collect_externs(obj);
@@ -9684,6 +9748,12 @@ void bpf_object__close(struct bpf_object *obj)
close(obj->jumptable_maps[i].fd);
zfree(&obj->jumptable_maps);
+ if (obj->btf_module_allowlist) {
+ for (i = 0; i < obj->btf_module_allowlist_cnt; i++)
+ zfree(&obj->btf_module_allowlist[i]);
+ zfree(&obj->btf_module_allowlist);
+ }
+
free(obj);
}
diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
index b965ad571540..89768244d5d9 100644
--- a/tools/lib/bpf/libbpf.h
+++ b/tools/lib/bpf/libbpf.h
@@ -224,10 +224,24 @@ struct bpf_object_open_opts {
* point (/sys/fs/bpf), in case this default behavior is undesirable.
*/
const char *bpf_token_path;
+ /*
+ * Optional allowlist of kernel module names whose BTFs libbpf is
+ * allowed to load. The allowlist limits which kernel module BTFs libbpf
+ * will consult wherever module BTF might be needed.
+ *
+ * When the option is not specified, the existing behavior remains
+ * unchanged. An explicitly specified empty list prevents libbpf from
+ * consulting any kernel module BTFs.
+ *
+ * The list must contain valid, non-empty module names and must not
+ * contain duplicate entries; otherwise -EINVAL is returned.
+ */
+ const char **btf_module_allowlist;
+ size_t btf_module_allowlist_cnt;
size_t :0;
};
-#define bpf_object_open_opts__last_field bpf_token_path
+#define bpf_object_open_opts__last_field btf_module_allowlist_cnt
/**
* @brief **bpf_object__open()** creates a bpf_object by opening
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH bpf-next v9 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
2026-09-15 12:41 ` [PATCH bpf-next v9 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
@ 2026-09-15 13:33 ` bot+bpf-ci
0 siblings, 0 replies; 6+ messages in thread
From: bot+bpf-ci @ 2026-09-15 13:33 UTC (permalink / raw)
To: zhaofuyu, bpf
Cc: eddyz87, andrii.nakryiko, alan.maguire, olsajiri, zhaofuyu,
andrii, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song,
mason, ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 4752 bytes --]
> diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c
> index 613afae26519..d6f297bd5dfb 100644
> --- a/tools/lib/bpf/libbpf.c
> +++ b/tools/lib/bpf/libbpf.c
[ ... ]
> @@ -5838,6 +5840,21 @@ int bpf_core_add_cands(struct bpf_core_cand *local_cand,
> return 0;
> }
>
> +static bool is_btf_mod_allowed(const struct bpf_object *obj, const char *name)
> +{
> + ssize_t i;
> +
> + if (obj->btf_module_allowlist_cnt < 0)
> + return true;
> +
> + for (i = 0; i < obj->btf_module_allowlist_cnt; i++) {
> + if (strcmp(obj->btf_module_allowlist[i], name) == 0)
> + return true;
> + }
> +
> + return false;
> +}
> +
> static int load_module_btfs(struct bpf_object *obj)
> {
> struct bpf_btf_info info;
> @@ -5860,6 +5877,9 @@ static int load_module_btfs(struct bpf_object *obj)
> if (!kernel_supports(obj, FEAT_MODULE_BTF))
> return 0;
>
> + if (obj->btf_module_allowlist_cnt == 0)
> + return 0;
> +
> while (true) {
> err = bpf_btf_get_next_id(id, &id);
> if (err && errno == ENOENT)
> @@ -5902,6 +5922,11 @@ static int load_module_btfs(struct bpf_object *obj)
> continue;
> }
>
> + if (!is_btf_mod_allowed(obj, name)) {
> + close(fd);
> + continue;
> + }
> +
> btf = btf_get_from_fd(fd, obj->btf_vmlinux);
[ ... ]
> @@ -5926,6 +5951,9 @@ static int load_module_btfs(struct bpf_object *obj)
> break;
> }
> obj->btf_module_cnt++;
> +
> + if (obj->btf_module_allowlist_cnt == obj->btf_module_cnt)
> + break;
> }
In an earlier review thread for v6, Alan Maguire suggested adding optional
flexibility to distinguish between modules that must exist versus modules
that are optional. He proposed a required_module_cnt parameter to handle
scenarios where a module might be built-in ('y') on one kernel and a
module ('m') on another:
"it might be good to retain the non-existent module tolerance optionally
somehow (a required_module_cnt perhaps?) since it's often the case that
a module is built-in on one kernel and a module on another. If a module
is builtin ("y" instead of "m"), having a way to say "load this if it
is a module but don't fail if it is not present" would be handy, since
the needed BTF would then be in vmlinux BTF rather than module BTF."
https://lore.kernel.org/bpf/b2602c85-c5ad-42d9-9f64-1bb7e1100a85@oracle.com/
While you initially agreed to add module count verification, was the
follow-up enhancement suggestion for optional required_module_cnt
deliberately deferred, or should it be considered for this version?
> diff --git a/tools/lib/bpf/libbpf.h b/tools/lib/bpf/libbpf.h
> index 48266e752223..d3415a0671a8 100644
> --- a/tools/lib/bpf/libbpf.h
> +++ b/tools/lib/bpf/libbpf.h
> @@ -224,10 +224,24 @@ struct bpf_object_open_opts {
> * point (/sys/fs/bpf), in case this default behavior is undesirable.
> */
> const char *bpf_token_path;
> + /*
> + * Optional allowlist of kernel module names whose BTFs libbpf is
> + * allowed to load. The allowlist limits which kernel module BTFs libbpf
> + * will consult wherever module BTF might be needed.
> + *
> + * When the option is not specified, the existing behavior remains
> + * unchanged. An explicitly specified empty list prevents libbpf from
> + * consulting any kernel module BTFs.
> + *
> + * The list must contain valid, non-empty module names and must not
> + * contain duplicate entries; otherwise -EINVAL is returned.
> + */
> + const char **btf_module_allowlist;
> + size_t btf_module_allowlist_cnt;
Does the documentation accurately describe how the empty list case works?
The implementation distinguishes three states purely by the pointer, not
the count:
obj->btf_module_allowlist_cnt = mod_allow ? mod_allow_cnt : -1;
so btf_module_allowlist == NULL yields -1, which is_btf_mod_allowed()
treats as 'allow everything'. The only way to reach the documented
empty-list state is to set btf_module_allowlist to some non-NULL pointer
while leaving btf_module_allowlist_cnt at 0, but C has no zero-length
array literal to point it at. Should the comment explain this explicitly?
Also, does the error handling match what the comment describes? The
comment says "otherwise -EINVAL is returned" but bpf_object__open_file()
and bpf_object__open_mem() return NULL with errno == EINVAL rather than
-EINVAL directly:
return libbpf_ptr(bpf_object_open(...));
Only bpf_object__open_skeleton() actually returns -EINVAL. Should the
documentation clarify that the *_open*() functions set errno rather than
returning a negative value?
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/34972296720
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH bpf-next v9 2/2] selftests/bpf: add tests for selective module BTF loading
2026-09-15 12:41 [PATCH bpf-next v9 0/2] libbpf: improve BPF load performance by selectively loading module BTFs Fuyu Zhao
2026-09-15 12:41 ` [PATCH bpf-next v9 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
@ 2026-09-15 12:41 ` Fuyu Zhao
2026-09-15 13:33 ` bot+bpf-ci
2026-09-21 16:40 ` [PATCH bpf-next v9 0/2] libbpf: improve BPF load performance by selectively loading module BTFs patchwork-bot+netdevbpf
2 siblings, 1 reply; 6+ messages in thread
From: Fuyu Zhao @ 2026-09-15 12:41 UTC (permalink / raw)
To: bpf; +Cc: eddyz87, andrii.nakryiko, alan.maguire, olsajiri, Fuyu Zhao
Add selftests covering selective kernel module BTF loading through
bpf_object_open_opts.
The tests verify that:
- the existing behavior is preserved when the allowlist is not
specified;
- loading succeeds when the required module BTF is specified in the
allowlist;
- module BTFs not in the allowlist are skipped;
- an empty allowlist skips loading all module BTFs;
- invalid allowlist and module name inputs are rejected.
Signed-off-by: Fuyu Zhao <zhaofuyu@vivo.com>
---
.../bpf/prog_tests/btf_module_allowlist.c | 162 ++++++++++++++++++
.../bpf/progs/btf_module_allowlist.c | 13 ++
2 files changed, 175 insertions(+)
create mode 100644 tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c
create mode 100644 tools/testing/selftests/bpf/progs/btf_module_allowlist.c
diff --git a/tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c b/tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c
new file mode 100644
index 000000000000..3189133ee58b
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c
@@ -0,0 +1,162 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <test_progs.h>
+#include <bpf/btf.h>
+#include "btf_module_allowlist.skel.h"
+
+static void test_load_with_list(const char **list, size_t count,
+ int expected_ret)
+{
+ struct btf_module_allowlist *skel;
+ int ret;
+ LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .btf_module_allowlist = list,
+ .btf_module_allowlist_cnt = count,
+ );
+
+ skel = btf_module_allowlist__open_opts(&opts);
+ if (!ASSERT_OK_PTR(skel, "btf_module_allowlist__open_opts"))
+ return;
+
+ ret = btf_module_allowlist__load(skel);
+ ASSERT_EQ(ret, expected_ret, "btf_module_allowlist__load");
+
+ btf_module_allowlist__destroy(skel);
+}
+
+static void btf_module_allowlist_default(void)
+{
+ struct btf_module_allowlist *skel;
+ int ret;
+
+ skel = btf_module_allowlist__open();
+ if (!ASSERT_OK_PTR(skel, "btf_module_allowlist__open"))
+ return;
+
+ ret = btf_module_allowlist__load(skel);
+ ASSERT_OK(ret, "btf_module_allowlist__load");
+
+ btf_module_allowlist__destroy(skel);
+}
+
+static void btf_module_allowlist_allow(void)
+{
+ const char *mod_list[] = { "bpf_testmod" };
+
+ test_load_with_list(mod_list, 1, 0);
+}
+
+/*
+ * A non-NULL allowlist with a count of 0 explicitly disables module BTFs.
+ * The pointer must be non-NULL to distinguish this from an unspecified
+ * allowlist.
+ */
+static void btf_module_allowlist_emptylist(void)
+{
+ const char *mod_list[] = { NULL };
+
+ test_load_with_list(mod_list, 0, -ESRCH);
+}
+
+/*
+ * bpf_testmod is not in the allowlist, so its BTF should not be loaded.
+ */
+static void btf_module_allowlist_skipunlisted(void)
+{
+ const char *mod_list[] = { "module_nonexist" };
+ struct btf_module_allowlist *skel;
+ int ret;
+ LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .btf_module_allowlist = mod_list,
+ .btf_module_allowlist_cnt = 1,
+ );
+
+ skel = btf_module_allowlist__open_opts(&opts);
+ if (!ASSERT_OK_PTR(skel, "btf_module_allowlist__open_opts"))
+ return;
+
+ ret = bpf_program__set_attach_target(skel->progs.test_btf_module_allowlist, 0,
+ "bpf_testmod:bpf_testmod_fentry_test1");
+ ASSERT_EQ(ret, -ESRCH, "bpf_program__set_attach_target");
+ btf_module_allowlist__destroy(skel);
+}
+
+static void test_invalid_input(const char **list, size_t count,
+ const char *test_name)
+{
+ struct btf_module_allowlist *skel;
+ char assert_name[64];
+ int err;
+ LIBBPF_OPTS(bpf_object_open_opts, opts,
+ .btf_module_allowlist = list,
+ .btf_module_allowlist_cnt = count,
+ );
+
+ snprintf(assert_name, sizeof(assert_name), "%s: open_opts", test_name);
+ skel = btf_module_allowlist__open_opts(&opts);
+ err = errno;
+ if (!ASSERT_NULL(skel, assert_name)) {
+ btf_module_allowlist__destroy(skel);
+ return;
+ }
+ snprintf(assert_name, sizeof(assert_name), "%s: open_opts err", test_name);
+ ASSERT_EQ(err, EINVAL, assert_name);
+}
+
+static void btf_module_allowlist_invalidinput(void)
+{
+ const char *names[] = { NULL };
+ const char *empty_names[] = { "" };
+ const char *duplicate_names[] = { "bpf_testmod", "bpf_testmod" };
+
+ test_invalid_input(NULL, 1, "null_list");
+ test_invalid_input(names, 1, "null_name");
+ test_invalid_input(empty_names, 1, "empty_name");
+ test_invalid_input(duplicate_names, 2, "duplicate_name");
+ test_invalid_input(names, (size_t)SSIZE_MAX + 1, "large_count");
+}
+
+void test_btf_module_allowlist(void)
+{
+ struct btf *vmlinux_btf = NULL;
+ struct btf *module_btf = NULL;
+
+ if (!env.has_testmod) {
+ printf("%s:SKIP: bpf_testmod is not available\n", __func__);
+ test__skip();
+ return;
+ }
+
+ vmlinux_btf = btf__load_vmlinux_btf();
+ if (!vmlinux_btf) {
+ printf("%s:SKIP: vmlinux_btf is not available\n", __func__);
+ test__skip();
+ return;
+ }
+
+ /* Ensure bpf_testmod BTF is available. */
+ module_btf = btf__load_module_btf("bpf_testmod", vmlinux_btf);
+ if (!module_btf) {
+ printf("%s:SKIP: bpf_testmod's BTF is not available\n", __func__);
+ btf__free(vmlinux_btf);
+ test__skip();
+ return;
+ }
+
+ btf__free(module_btf);
+ btf__free(vmlinux_btf);
+
+ if (test__start_subtest("btf_module_allowlist_default"))
+ btf_module_allowlist_default();
+
+ if (test__start_subtest("btf_module_allowlist_allow"))
+ btf_module_allowlist_allow();
+
+ if (test__start_subtest("btf_module_allowlist_skipunlisted"))
+ btf_module_allowlist_skipunlisted();
+
+ if (test__start_subtest("btf_module_allowlist_emptylist"))
+ btf_module_allowlist_emptylist();
+
+ if (test__start_subtest("btf_module_allowlist_invalidinput"))
+ btf_module_allowlist_invalidinput();
+}
diff --git a/tools/testing/selftests/bpf/progs/btf_module_allowlist.c b/tools/testing/selftests/bpf/progs/btf_module_allowlist.c
new file mode 100644
index 000000000000..bb3c38bee77d
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/btf_module_allowlist.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <vmlinux.h>
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+
+SEC("fentry/bpf_testmod_loop_test")
+int BPF_PROG(test_btf_module_allowlist)
+{
+ return 0;
+}
+
+char _license[] SEC("license") = "GPL";
--
2.34.1
^ permalink raw reply related [flat|nested] 6+ messages in thread* Re: [PATCH bpf-next v9 2/2] selftests/bpf: add tests for selective module BTF loading
2026-09-15 12:41 ` [PATCH bpf-next v9 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
@ 2026-09-15 13:33 ` bot+bpf-ci
0 siblings, 0 replies; 6+ messages in thread
From: bot+bpf-ci @ 2026-09-15 13:33 UTC (permalink / raw)
To: zhaofuyu, bpf
Cc: eddyz87, andrii.nakryiko, alan.maguire, olsajiri, zhaofuyu, ast,
andrii, daniel, martin.lau, eddyz87, yonghong.song, mason,
ihor.solodrai
[-- Attachment #1: Type: text/plain, Size: 3254 bytes --]
> diff --git a/tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c b/tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c
> new file mode 100644
> index 000000000000..3189133ee58b
> --- /dev/null
> +++ b/tools/testing/selftests/bpf/prog_tests/btf_module_allowlist.c
> @@ -0,0 +1,162 @@
> +// SPDX-License-Identifier: GPL-2.0
> +#include <test_progs.h>
> +#include <bpf/btf.h>
> +#include "btf_module_allowlist.skel.h"
[ ... ]
> +static void test_invalid_input(const char **list, size_t count,
> + const char *test_name)
> +{
> + struct btf_module_allowlist *skel;
> + char assert_name[64];
> + int err;
> + LIBBPF_OPTS(bpf_object_open_opts, opts,
> + .btf_module_allowlist = list,
> + .btf_module_allowlist_cnt = count,
> + );
> +
> + snprintf(assert_name, sizeof(assert_name), "%s: open_opts", test_name);
> + skel = btf_module_allowlist__open_opts(&opts);
> + err = errno;
> + if (!ASSERT_NULL(skel, assert_name)) {
> + btf_module_allowlist__destroy(skel);
> + return;
> + }
> + snprintf(assert_name, sizeof(assert_name), "%s: open_opts err", test_name);
> + ASSERT_EQ(err, EINVAL, assert_name);
> +}
> +
> +static void btf_module_allowlist_invalidinput(void)
> +{
> + const char *names[] = { NULL };
> + const char *empty_names[] = { "" };
> + const char *duplicate_names[] = { "bpf_testmod", "bpf_testmod" };
> +
> + test_invalid_input(NULL, 1, "null_list");
> + test_invalid_input(names, 1, "null_name");
> + test_invalid_input(empty_names, 1, "empty_name");
> + test_invalid_input(duplicate_names, 2, "duplicate_name");
> + test_invalid_input(names, (size_t)SSIZE_MAX + 1, "large_count");
^^^^^^^
Does this test actually verify the count-overflow guard? The "large_count"
case reuses names[] = { NULL } as the allowlist, but the validation in
bpf_object_open() (from patch 1, tools/lib/bpf/libbpf.c) has two
independent paths to -EINVAL for that input:
if (mod_allow_cnt > SSIZE_MAX || (!mod_allow && mod_allow_cnt > 0))
return ERR_PTR(-EINVAL);
for (i = 0; i < mod_allow_cnt; i++) {
if (!mod_allow[i] || !mod_allow[i][0])
return ERR_PTR(-EINVAL);
If the mod_allow_cnt > SSIZE_MAX guard were dropped, the loop would still
return -EINVAL on i == 0 because names[0] is NULL, so the assertion passes
and the count-overflow check goes untested. The overflow guard is the only
thing keeping obj->btf_module_allowlist_cnt (ssize_t) from being assigned a
negative value from a size_t count, which is_btf_mod_allowed() interprets
as "allowlist unspecified, allow everything."
The other four cases each pin exactly one check (null_list tests !mod_allow,
null_name tests !mod_allow[i], empty_name tests !mod_allow[i][0],
duplicate_name tests the strcmp loop). Would passing a list of valid
non-empty names like duplicate_names[0] (i.e., { "bpf_testmod" }) with the
oversized count discriminate the count check from the unrelated precondition
check?
> +}
[ ... ]
---
AI reviewed your patch. Please fix the bug or email reply why it's not a bug.
See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md
CI run summary: https://github.com/kernel-patches/bpf/actions/runs/34972296720
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH bpf-next v9 0/2] libbpf: improve BPF load performance by selectively loading module BTFs
2026-09-15 12:41 [PATCH bpf-next v9 0/2] libbpf: improve BPF load performance by selectively loading module BTFs Fuyu Zhao
2026-09-15 12:41 ` [PATCH bpf-next v9 1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts Fuyu Zhao
2026-09-15 12:41 ` [PATCH bpf-next v9 2/2] selftests/bpf: add tests for selective module BTF loading Fuyu Zhao
@ 2026-09-21 16:40 ` patchwork-bot+netdevbpf
2 siblings, 0 replies; 6+ messages in thread
From: patchwork-bot+netdevbpf @ 2026-09-21 16:40 UTC (permalink / raw)
To: Fuyu Zhao; +Cc: bpf, eddyz87, andrii.nakryiko, alan.maguire, olsajiri
Hello:
This series was applied to bpf/bpf-next.git (master)
by Andrii Nakryiko <andrii@kernel.org>:
On Tue, 15 Sep 2026 20:41:02 +0800 you wrote:
> Currently, load_module_btfs() unconditionally iterates over all kernel
> module BTFs and loads each one. This introduces unnecessary overhead
> when a BPF program only needs BTFs from a small subset of modules.
>
> In our Android testing, BPF programs are loaded on demand rather than
> preloaded to avoid unnecessary memory usage from unused programs. With
> 93 module BTFs present, the total BPF loading time exceeds 300 ms, with
> module BTF loading accounting for around 69% of the total loading time.
>
> [...]
Here is the summary with links:
- [bpf-next,v9,1/2] libbpf: support selective kernel module BTF loading via bpf_object_open_opts
https://git.kernel.org/bpf/bpf-next/c/34b184792443
- [bpf-next,v9,2/2] selftests/bpf: add tests for selective module BTF loading
https://git.kernel.org/bpf/bpf-next/c/66c22d8c0f62
You are awesome, thank you!
--
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html
^ permalink raw reply [flat|nested] 6+ messages in thread