Re: [RFC PATCH bpf-next 1/2] libbpf: support selective kernel module BTF loading via .kmod_btfs section
Fuyu Zhao <[email protected]> Thu, 6 Aug 2026 16:38:25 +0800
| Newsgroups | dev.linux.lists.sashiko-reviews,org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
On 8/6/2026 12:33 PM, [email protected] 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 <[email protected]> > > 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; >> +} >> + >