selftests/bpf: Add tests for selective module BTF loading

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>
Signed-off-by: Andrii Nakryiko <andrii@kernel.org>
Link: https://lore.kernel.org/bpf/20260915124104.77287-3-zhaofuyu@vivo.com
This commit is contained in:
Fuyu Zhao 2026-09-15 20:41:04 +08:00 committed by Andrii Nakryiko
parent 34b1847924
commit 66c22d8c0f
2 changed files with 175 additions and 0 deletions

View File

@ -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();
}

View File

@ -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";