[PATCH bpf-next 06/13] bpf: resolve loader-style kfunc CALLs against prog BTF

Alexei Starovoitov alexei.starovoitov at gmail.com
Sat May 23 17:01:20 UTC 2026


On Fri, May 22, 2026 at 4:32 AM KP Singh <kpsingh at kernel.org> wrote:
>
> gen_loader-emitted signed loaders cannot bake vmlinux BTF ids into
> kfunc CALL imm at sign time. Add a new pseudo
> BPF_PSEUDO_KFUNC_CALL_PROG_BTF that gen_loader emits in src_reg, with
> imm holding the FUNC type id in the loader's own prog BTF.
>
> In add_subprog_and_kfunc, look the FUNC up by name in vmlinux BTF,
> patch imm with the resolved id, and rewrite src_reg back to
> BPF_PSEUDO_KFUNC_CALL so downstream passes see a normal kfunc CALL.
> Leave standard src_reg calls unchanged.
>
> Signed-off-by: KP Singh <kpsingh at kernel.org>
> ---
>  include/linux/bpf_verifier.h   |  6 ++++
>  include/uapi/linux/bpf.h       |  5 ++++
>  kernel/bpf/verifier.c          | 54 ++++++++++++++++++++++++++++++++--
>  tools/include/uapi/linux/bpf.h |  5 ++++
>  4 files changed, 67 insertions(+), 3 deletions(-)
>
> diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
> index 185b2aa43a42..396b85830996 100644
> --- a/include/linux/bpf_verifier.h
> +++ b/include/linux/bpf_verifier.h
> @@ -959,6 +959,12 @@ static inline bool bpf_pseudo_kfunc_call(const struct bpf_insn *insn)
>                insn->src_reg == BPF_PSEUDO_KFUNC_CALL;
>  }
>
> +static inline bool bpf_pseudo_kfunc_call_prog_btf(const struct bpf_insn *insn)
> +{
> +       return insn->code == (BPF_JMP | BPF_CALL) &&
> +              insn->src_reg == BPF_PSEUDO_KFUNC_CALL_PROG_BTF;
> +}
> +
>  __printf(2, 0) void bpf_verifier_vlog(struct bpf_verifier_log *log,
>                                       const char *fmt, va_list args);
>  __printf(2, 3) void bpf_verifier_log_write(struct bpf_verifier_env *env,
> diff --git a/include/uapi/linux/bpf.h b/include/uapi/linux/bpf.h
> index 552bc5d9afbd..06056e714e8e 100644
> --- a/include/uapi/linux/bpf.h
> +++ b/include/uapi/linux/bpf.h
> @@ -1382,6 +1382,11 @@ enum {
>   * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the running kernel
>   */
>  #define BPF_PSEUDO_KFUNC_CALL  2
> +/* when bpf_call->src_reg == BPF_PSEUDO_KFUNC_CALL_PROG_BTF,
> + * bpf_call->imm == btf_id of a BTF_KIND_FUNC in the program's
> + * prog BTF. The verifier resolves it to a vmlinux btf_id by name.
> + */
> +#define BPF_PSEUDO_KFUNC_CALL_PROG_BTF 3
>
>  enum bpf_addr_space_cast {
>         BPF_ADDR_SPACE_CAST = 1,
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index f0e45cfa5b34..1b5d06b9d74a 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -3088,6 +3088,47 @@ bool bpf_prog_has_kfunc_call(const struct bpf_prog *prog)
>         return !!prog->aux->kfunc_tab;
>  }
>
> +/*
> + * Resolve a gen_loader-emitted kfunc CALL by FUNC name in vmlinux BTF,
> + * then rewrite src_reg back to BPF_PSEUDO_KFUNC_CALL. Caller must have
> + * already filtered for BPF_PSEUDO_KFUNC_CALL_PROG_BTF.
> + */
> +static int resolve_loader_kfunc(struct bpf_verifier_env *env,
> +                               struct bpf_insn *insn, int insn_idx)
> +{
> +       struct btf *prog_btf = env->prog->aux->btf;
> +       const struct btf_type *t;
> +       const char *name;
> +       s32 vmlinux_id;
> +
> +       if (!prog_btf || !btf_vmlinux || insn->off) {
> +               verbose(env, "kfunc call insn %d: PROG_BTF resolution requires prog BTF and insn->off == 0\n",
> +                       insn_idx);
> +               return -EINVAL;
> +       }
> +       t = btf_type_by_id(prog_btf, insn->imm);
> +       if (!t || !btf_type_is_func(t)) {
> +               verbose(env, "kfunc call insn %d: imm %d is not a FUNC in prog BTF\n",
> +                       insn_idx, insn->imm);
> +               return -EINVAL;
> +       }
> +       name = btf_name_by_offset(prog_btf, t->name_off);
> +       if (!name || !name[0]) {
> +               verbose(env, "kfunc call insn %d: prog-BTF FUNC has no name\n",
> +                       insn_idx);
> +               return -EINVAL;
> +       }
> +       vmlinux_id = btf_find_by_name_kind(btf_vmlinux, name, BTF_KIND_FUNC);
> +       if (vmlinux_id < 0) {
> +               verbose(env, "kfunc call insn %d: %s not found in vmlinux BTF\n",
> +                       insn_idx, name);
> +               return vmlinux_id;
> +       }
> +       insn->imm = vmlinux_id;
> +       insn->src_reg = BPF_PSEUDO_KFUNC_CALL;
> +       return 0;

Ohh. So this patch does it.
Probably worth squashing the patches then?

Also since the resolution into kernel kfunc is by name only,
it feels odd to create a fake prog BTF proto just for the name.
What is an alternative?

Also note that we were discussing allowing the replacement of kfuncs
by modules with the same name. This kfunc should be
immutable?



More information about the Linux-security-module-archive mailing list