Thank you for your contribution! Sashiko AI review found 5 potential issue(s) 
to consider:
- [High] bpf: crash on blind dereference of ERR_PTR from bpf_get_btf_vmlinux()
- [High] bpf: single vmlinux BTF parse failure permanently bricks verifier
- [Medium] bpf: context translation fails for EXT/TRACING progs without vmlinux 
BTF
- [Medium] bpf: AB-BA deadlock between bpf_verifier_lock and request_module()
- [Medium] bpf: bpf_snprintf_btf fails because vmlinux BTF load is skipped
--

commit 68de9774165f5e9dfaacde093071f89754bece4b
Author: Jay Wang <[email protected]>

bpf: fetch the vmlinux BTF where kernel types enter a program

This commit delays the fetching and parsing of vmlinux BTF in the BPF
verifier, moving it from an upfront operation for all programs to an
on-demand fetch. This prevents unnecessary module loading and memory usage
for BPF programs that do not rely on kernel types.

> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index a7c9e2d8965d5..2425ea74b61d6 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -2873,7 +2873,8 @@ int bpf_add_kfunc_call(struct bpf_verifier_env *env, 
> u32 func_id, u16 offset)
>       tab = prog_aux->kfunc_tab;
>       btf_tab = prog_aux->kfunc_btf_tab;
>       if (!tab) {
> -             if (!btf_vmlinux) {
> +             /* with CONFIG_DEBUG_INFO_BTF=m this is where the vmlinux BTF 
> gets loaded */
> +             if (IS_ERR_OR_NULL(bpf_get_btf_vmlinux())) {

[Severity: Medium]
Could this introduce an AB-BA deadlock with the module loader?

For unprivileged programs, bpf_check() acquires bpf_verifier_lock before
executing instruction verification. When CONFIG_DEBUG_INFO_BTF=m, this
on-demand fetch calls request_module() which blocks and spawns a usermode
helper (modprobe).

If the usermode helper (or another user-space process triggered during the
module load) attempts to load an unprivileged BPF program, it will block
indefinitely waiting for bpf_verifier_lock.

[ ... ]

> @@ -11568,6 +11570,20 @@ static int release_reg(struct bpf_verifier_env *env, 
> struct bpf_reg_state *reg,
>       return err;
>  }
>  
> +/* Does calling @fn bring kernel BTF types into the program state? */
> +static bool helper_uses_vmlinux_btf(const struct bpf_func_proto *fn)
> +{
> +     int i;
> +
> +     if (base_type(fn->ret_type) == RET_PTR_TO_BTF_ID)
> +             return true;
> +     for (i = 0; i < MAX_BPF_FUNC_ARGS; i++) {
> +             if (base_type(fn->arg_type[i]) == ARG_PTR_TO_BTF_ID)
> +                     return true;
> +     }
> +     return false;
> +}

[Severity: Medium]
Does this logic miss helpers that require the vmlinux BTF but don't explicitly
take or return a PTR_TO_BTF_ID?

For instance, the bpf_snprintf_btf helper takes a struct btf_ptr * as an
ARG_PTR_TO_MEM. Because this isn't caught by helper_uses_vmlinux_btf(), the
verifier skips loading the BTF.

At runtime, the helper calls bpf_peek_btf_vmlinux(). Since the module was
never loaded by the verifier, this returns NULL and the helper functionally
fails with -EINVAL, potentially breaking valid BPF programs that rely on it.

[ ... ]

> @@ -21724,7 +21762,11 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr 
> *attr, bpfptr_t uattr,
>       if (ret)
>               goto err_prep;
>  
> -     bpf_get_btf_vmlinux();
> +     /*
> +      * The vmlinux BTF is not fetched up front: with CONFIG_DEBUG_INFO_BTF=m
> +      * it is loaded on demand, at the points where kernel types enter the
> +      * program (attach_btf, kfuncs, ksyms, map pointers, BTF-typed helpers).
> +      */

[Severity: High]
Can a single transient module load or parse failure permanently brick the
verifier?

If bpf_get_btf_vmlinux() fails (e.g., due to ENOMEM or a temporary module load
failure), btf_parse_vmlinux() returns an ERR_PTR which is permanently cached
in btf_vmlinux via smp_store_release(&btf_vmlinux, btf).

The verifier unconditionally checks if (IS_ERR(btf_vmlinux)) at the beginning
of bpf_check(). Consequently, a single transient failure permanently breaks the
loading of all subsequent BPF programs system-wide, including those that do not
require BTF.

[Severity: Medium]
Does removing this upfront fetch break context translation for EXT and TRACING
programs?

When loading a BPF_PROG_TYPE_EXT or BPF_PROG_TYPE_TRACING program targeting
another BPF program, bpf_prog_load() skips fetching the vmlinux BTF. If the
program does not trigger an on-demand BTF load via kfuncs or helpers, the
global bpf_ctx_convert.t remains NULL.

During verification, btf_ctx_access() calls btf_translate_to_vmlinux(), which
uses find_canonical_prog_ctx_type(). Because bpf_ctx_convert.t is NULL,
translation fails and the verifier incorrectly rejects valid context accesses.

[Severity: High]
Does delaying the BTF load expose paths to blind ERR_PTR dereferences?

Because the patch makes the BTF load lazy, it is more likely to fail much later
in the verification process, exposing paths that blindly dereference the
pointer returned by bpf_get_btf_vmlinux() without checking IS_ERR().

For example, btf_prepare_func_args() does:

    vmlinux_btf = bpf_get_btf_vmlinux();
    ref_t = btf_type_by_id(vmlinux_btf, kern_type_id);

And btf_check_kfunc_name() does:

    id = btf_find_by_name_kind(bpf_get_btf_vmlinux(), func_name, kind);

If bpf_get_btf_vmlinux() returns an ERR_PTR, passing it directly to these
functions will result in a kernel panic.

-- 
Sashiko AI review ยท 
https://sashiko.dev/#/patchset/[email protected]?part=3

Reply via email to