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 Signed-off-by: Fuyu Zhao --- 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