> diff --git a/include/linux/bpf.h b/include/linux/bpf.h > index e7c5e203eddd..a3c4caad5dfc 100644 > --- a/include/linux/bpf.h > +++ b/include/linux/bpf.h > @@ -3165,6 +3165,7 @@ static inline s32 bpf_call_args_imm(s16 idx) > #endif > > struct btf *bpf_get_btf_vmlinux(void); > +struct btf *bpf_peek_btf_vmlinux(void); > > /* Map specifics */ > struct xdp_frame; [ ... ] > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index a7c9e2d8965d..2425ea74b61d 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c [ ... ] > @@ -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). > + */ > > /* Serialize verification of unprivileged programs. */ > if (!is_priv) Does removing this unconditional bpf_get_btf_vmlinux() call break program-context type resolution? The removed call was the sole caller of btf_parse_vmlinux(), which is the only place that populates the file-scope bpf_ctx_convert.t table needed by find_canonical_prog_ctx_type(). On a CONFIG_DEBUG_INFO_BTF=m kernel where the BTF hasn't been parsed yet, consider this chain: bpf_check() -> do_check_subprogs() -> do_check_common() -> btf_prepare_func_args() -> btf_is_prog_ctx_type() -> find_canonical_prog_ctx_type() At this point, find_canonical_prog_ctx_type() returns NULL because conv_struct = bpf_ctx_convert.t is still NULL. Then btf_is_prog_ctx_type() logs "btf_vmlinux is malformed" and returns false. A global subprog argument like 'struct __sk_buff *skb' or 'struct xdp_md *ctx' then falls through to the 'generic user data pointer' branch and gets typed ARG_PTR_TO_MEM instead of ARG_PTR_TO_CTX. At the call site btf_check_func_arg_match() -> check_mem_reg() rejects the caller's PTR_TO_CTX register with a confusing type error. When libbpf emits the common 'arg:ctx' decl tag, btf_prepare_func_args() -> btf_validate_prog_ctx_type() returns -EINVAL directly with "btf_vmlinux is malformed". The same NULL bpf_ctx_convert.t also breaks btf_ctx_access() -> btf_translate_to_vmlinux() for BPF_PROG_TYPE_EXT programs loaded with attach_prog_fd, so ctx member access in the replacement program fails. The changelog lists the new fetch sites: kfuncs, ksyms, map_ptr access, BTF-typed helpers, attach_btf. None of these cover the program-context path, which needs the vmlinux BTF but never touches a PTR_TO_BTF_ID register. The claim that "every PTR_TO_BTF_ID register a program can hold originates from one of these sites" is true but insufficient as a coverage argument. The failure is non-deterministic: if anything else (a kfunc-using program, mounting bpffs, BPF_BTF_GET_NEXT_ID) already forced the parse, the same program loads fine. Programs that work on CONFIG_DEBUG_INFO_BTF=y intermittently fail on =m. Commits later in this series make this reachable: d80d57b4ec13 defers kfunc registration until the BTF is available (removing the boot-time parse from the first kfunc registration), and 2f5aab95d2b8 makes CONFIG_DEBUG_INFO_BTF=m selectable. A search of the forward range 90fe51b6b36a..2f5aab95d2b8 for bpf_ctx_convert, find_canonical_prog_ctx_type, btf_is_prog_ctx_type and btf_prepare_func_args finds only comment/documentation text - no fix. > @@ -21790,0 +21832,5 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr, > if (IS_ERR(btf_vmlinux)) { > /* Either gcc or pahole or kernel are broken. */ > verbose(env, "in-kernel BTF is malformed\n"); > ret = PTR_ERR(btf_vmlinux); > goto skip_full_check; > } This check a few lines below the deleted fetch was the consumer of that fetch: bpf_get_btf_vmlinux() stores an error pointer with smp_store_release(&btf_vmlinux, btf) on parse failure, and bpf_check() then converted it into the "in-kernel BTF is malformed" diagnostic. With the fetch gone, this check reads a global that this program hasn't caused to be populated. Whether a malformed-BTF kernel reports the clear error or a confusing downstream one now depends on whether some earlier, unrelated program already triggered the parse. It also reads btf_vmlinux directly rather than through the new bpf_peek_btf_vmlinux() accessor, so the load is not ordered against the smp_store_release() that publishes it. Could this check either move next to a real fetch or be dropped and let the per-site IS_ERR_OR_NULL() handling report the failure? > diff --git a/kernel/trace/bpf_trace.c b/kernel/trace/bpf_trace.c > index 195f78db9bda..c022b2877f0b 100644 > --- a/kernel/trace/bpf_trace.c > +++ b/kernel/trace/bpf_trace.c > @@ -1015,7 +1015,8 @@ static int bpf_btf_printf_prepare(struct btf_ptr *ptr, u32 btf_ptr_size, > if (btf_ptr_size != sizeof(struct btf_ptr)) > return -EINVAL; > > - *btf = bpf_get_btf_vmlinux(); > + /* Called from a running program: only use the BTF if it is parsed. */ > + *btf = bpf_peek_btf_vmlinux(); > > if (IS_ERR_OR_NULL(*btf)) > return IS_ERR(*btf) ? PTR_ERR(*btf) : -EINVAL; Swapping bpf_get_btf_vmlinux() for bpf_peek_btf_vmlinux() is correct as a sleeping-in-program-context fix, but does anything in this commit arrange for the BTF to have been parsed by the time bpf_snprintf_btf() or bpf_seq_printf_btf() run? The compensating verifier-time fetch in check_helper_call() is gated on helper_uses_vmlinux_btf(), which only matches base_type(ret_type) == RET_PTR_TO_BTF_ID or base_type(arg_type[i]) == ARG_PTR_TO_BTF_ID. bpf_snprintf_btf_proto and bpf_seq_printf_btf_proto contain neither: their arguments are ARG_PTR_TO_MEM / ARG_CONST_SIZE / ARG_ANYTHING and the return is RET_INTEGER, because the BTF type id travels inside the struct btf_ptr the program passes by value rather than in a register type. So helper_uses_vmlinux_btf() returns false for exactly the two helpers whose runtime fetch was just weakened. On a CONFIG_DEBUG_INFO_BTF=m kernel where the BTF is present but not yet parsed, bpf_peek_btf_vmlinux() returns NULL and the helper returns -EINVAL. A working bpf_snprintf_btf() or bpf_seq_printf_btf() call silently starts failing, depending on whether unrelated activity already triggered the parse. The changelog frames this as behaving "as they do on a kernel without BTF", but the BTF is available, just unparsed, and the previous code would have produced correct output. Should the fix be at verify time - teaching helper_uses_vmlinux_btf(), or check_bpf_snprintf_call(), or the *_btf protos, to force the fetch? The forward range 90fe51b6b36a..2f5aab95d2b8 contains no code change for this. --- 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/35824427607