Re: [PATCH bpf-next v3 4/9] bpf: take the vmlinux BTF from the btf_vmlinux module
From: bot+bpf-ci
Date: Fri Sep 25 2026 - 19:24:23 EST
> diff --git a/include/linux/btf.h b/include/linux/btf.h
> index 4b63bb91550a..81e6c65fe5f6 100644
> --- a/include/linux/btf.h
> +++ b/include/linux/btf.h
> @@ -602,6 +602,7 @@ __u32 *btf_field_iter_next(struct btf_field_iter *it);
> const char *btf_name_by_offset(const struct btf *btf, u32 offset);
> const char *btf_str_by_offset(const struct btf *btf, u32 offset);
> struct btf *btf_parse_vmlinux(void);
> +void *btf_vmlinux_data(u32 *size, bool load);
> struct bpf_prog_get_target_btf(const struct bpf_prog *prog);
> u32 *btf_kfunc_flags(const struct btf *btf, u32 kfunc_btf_id, const struct bpf_prog *prog);
> int btf_kfunc_check_flag(const struct btf *btf, u32 kfunc_btf_id, u32 flag);
[ ... ]
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index da86162ba6d9..84968244a10f 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -21922,26 +21922,51 @@ int bpf_check_attach_btf_id_multi(struct btf *btf, struct bpf_prog *prog, u32 bt
> return 0;
> }
>
> +/*
> + * Returns the parsed vmlinux BTF, NULL if the kernel has none, or an ERR_PTR
> + * if it is malformed. With CONFIG_DEBUG_INFO_BTF=m the BTF lives in the
> + * btf_vmlinux module; the first caller loads it and parses it. May sleep.
> + */
> struct btf *bpf_get_btf_vmlinux(void)
> {
> /* Pairs with the smp_store_release() on the parse path below. */
> struct btf *btf = smp_load_acquire(&btf_vmlinux);
> + u32 size;
>
> - if (!btf && IS_ENABLED(CONFIG_DEBUG_INFO_BTF)) {
> - mutex_lock(&btf_vmlinux_lock);
> - btf = btf_vmlinux;
> - if (!btf) {
> - btf = btf_parse_vmlinux();
> - /*
> - * Order the parsed BTF contents and the globals the
> - * parse populated (e.g. bpf_ctx_convert.t) before
> - * the pointer publication. Pairs with the acquire
> - * on the lockless fast path above.
> - */
> - smp_store_release(&btf_vmlinux, btf);
> + if (btf || !IS_ENABLED(CONFIG_DEBUG_INFO_BTF))
> + return btf;
> +
> + /*
> + * Loading the module may take a while and its notifier must not be
> + * blocked by us, so do it outside btf_vmlinux_lock. Not available:
> + * behave like a kernel without BTF, and retry next time.
> + */
> + if (!btf_vmlinux_data(&size, true))
> + return NULL;
> +
> + mutex_lock(&btf_vmlinux_lock);
> + btf = btf_vmlinux;
> + if (!btf) {
> + btf = btf_parse_vmlinux();
> + /*
> + * With =m the BTF was checked against the kernel when the
> + * module loaded, so a failure here is a resource problem
> + * (-ENOMEM) rather than a broken BTF: do not remember it,
> + * the next caller retries.
> + */
> + if (IS_MODULE(CONFIG_DEBUG_INFO_BTF) && IS_ERR(btf)) {
> + mutex_unlock(&btf_vmlinux_lock);
> + return btf;
Does this error path account for deterministic parse failures?
The comment states that with CONFIG_DEBUG_INFO_BTF=m a parse failure
here is a resource problem (-ENOMEM) because the payload was checked
when the module loaded. btf_vmlinux_module_coming() does verify the
size and SHA-256 match the .BTF.meta values this kernel was built with.
However, btf_parse_vmlinux() calls btf_parse_base(), which can still
fail with errors that depend only on the content:
kernel/bpf/btf.c:btf_parse_base()
btf_parse_hdr() -> -E2BIG, -ENOENT, -EINVAL
btf_parse_str_sec() -> -EINVAL
btf_check_all_metas() -> -EINVAL for unknown kind or bad name
btf_check_modifier_chain_length() -> -ELOOP
These checks are deterministic. A broken BTF would fail on every
attempt, yet the code never caches the error. With CONFIG_DEBUG_INFO_BTF=y
the same condition gives 'in-kernel BTF is malformed' and the ERR_PTR
is cached once. With =m, every later caller repeats the full parse of
the ~5.4 MiB BTF while holding btf_vmlinux_lock: kvmalloc of the types
array, a walk over all metas, then the free.
Callers include bpf_prog_load(), struct_ops map creation,
BPF_BTF_GET_NEXT_ID (which runs before the CAP_SYS_ADMIN check in
bpf_obj_get_next_id()), and read()/mmap() of the 0444
/sys/kernel/btf/vmlinux. An unprivileged user can therefore make the
kernel re-parse repeatedly and serialize other BTF users behind the
mutex.
Also, /sys/kernel/btf/vmlinux returns -ENODEV even though the raw bytes
are present, because btf_sysfs_vmlinux_load() requires a successful
parse.
Would it be better to cache only on content errors (e.g. all errors
except -ENOMEM) and retry resource failures, or to skip caching only
on specific errors?
> }
> - mutex_unlock(&btf_vmlinux_lock);
> + /*
> + * Order the parsed BTF contents and the globals the
> + * parse populated (e.g. bpf_ctx_convert.t) before
> + * the pointer publication. Pairs with the acquire
> + * on the lockless fast path above.
> + */
> + smp_store_release(&btf_vmlinux, btf);
> }
> + mutex_unlock(&btf_vmlinux_lock);
> return btf;
> }
[ ... ]
---
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/36198628965