From f89ebf07496b041cb21beda60ec3452fecf34d10 Mon Sep 17 00:00:00 2001 From: Chris Mason Date: Sun, 1 Nov 2026 19:05:16 +0000 Subject: [PATCH] bpf: Reject memory for kfunc projection type args struct __sk_buff and struct xdp_md are projection types of struct sk_buff and struct xdp_buff. A kfunc that takes a projection type casts the argument to the underlying type. When the projection type is the ctx type of the prog, the verifier accepts only the ctx. In other progs, process_arg_ptr_to_btf_id() accepts a pointer to the underlying type. But this function identifies the underlying type only by its name. A struct of the same name in prog BTF also matches. Its objects are memory of the prog. This test also ignores the offset of the register. Thus the kfunc uses memory of the prog, or the middle of an skb, as an skb. Accept the underlying type only from kernel BTF, and only at offset 0. A prog that passes an skb of the kernel sees no change. struct xdp_md has only scalar members. Thus in a prog with another ctx type, resolve_func_arg_type() changes ARG_PTR_TO_BTF_ID to memory, and the verifier accepts memory of the prog. Keep ARG_PTR_TO_BTF_ID for projection types. The first Fixes tag is for this change. That commit merged the conntrack kfunc sets of two prog types with different ctx types. Thus bpf_xdp_ct_{alloc,lookup}() can use memory of the prog as an xdp_buff. Fixes: a4703e318432 ("bpf: Switch to new kfunc flags infrastructure") Fixes: ec209ad86324 ("bpf: verifier: Relax caller requirements for kfunc projection type args") Signed-off-by: Chris Mason Signed-off-by: Alexei Starovoitov Link: https://patch.msgid.link/20261101190535.1454146-9-alexei.starovoitov@gmail.com --- include/linux/btf.h | 1 + kernel/bpf/btf.c | 9 +++++++++ kernel/bpf/verifier.c | 16 +++++++++++++--- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/include/linux/btf.h b/include/linux/btf.h index a18254b7521b..87fad4076f0e 100644 --- a/include/linux/btf.h +++ b/include/linux/btf.h @@ -631,6 +631,7 @@ s32 btf_find_dtor_kfunc(struct btf *btf, u32 btf_id); int register_btf_id_dtor_kfuncs(const struct btf_id_dtor_kfunc *dtors, u32 add_cnt, struct module *owner); struct btf_struct_meta *btf_find_struct_meta(const struct btf *btf, u32 btf_id); +bool btf_is_projection_type(const char *pname); bool btf_is_projection_of(const char *pname, const char *tname); bool btf_is_prog_ctx_type(struct bpf_verifier_log *log, const struct btf *btf, const struct btf_type *t, enum bpf_prog_type prog_type, diff --git a/kernel/bpf/btf.c b/kernel/bpf/btf.c index 1be30487a2fa..fe24e459c1b1 100644 --- a/kernel/bpf/btf.c +++ b/kernel/bpf/btf.c @@ -6590,6 +6590,15 @@ static int find_kern_ctx_type_id(enum bpf_prog_type prog_type) return ctx_type->type; } +/* + * A projection type is the view that programs have of a kernel object with + * another layout: see btf_is_projection_of() for the two types behind them. + */ +bool btf_is_projection_type(const char *pname) +{ + return strcmp(pname, "__sk_buff") == 0 || strcmp(pname, "xdp_md") == 0; +} + bool btf_is_projection_of(const char *pname, const char *tname) { if (strcmp(pname, "__sk_buff") == 0 && strcmp(tname, "sk_buff") == 0) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index a90bc6c4033a..89c0f0304341 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -13585,7 +13585,13 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env, ref_t = btf_type_skip_modifiers(btf, ref_id, &ref_id); ref_tname = btf_name_by_offset(btf, ref_t->name_off); - if (!btf_type_is_scalar_struct(env, btf, ref_t)) + /* + * A kfunc that takes a projection type (ie. xdp_md) casts it to the + * underlying type. So memory of the program cannot stand in for it, + * even if the projection type holds only scalars. + */ + if (btf_is_projection_type(ref_tname) || + !btf_type_is_scalar_struct(env, btf, ref_t)) return 0; resolve_ret = btf_resolve_size(btf, ref_t, arg_size); @@ -14340,9 +14346,13 @@ static int process_arg_ptr_to_btf_id(struct bpf_verifier_env *env, struct bpf_re /* If kfunc is accepting a projection type (ie. __sk_buff), it cannot * actually use it -- it must cast to the underlying type. So we allow - * caller to pass in the underlying type. + * caller to pass in the underlying type. Only kernel BTF has that type: + * a type of the same name in the BTF of the program is program memory. + * And only a pointer to the start of the object points to that type. */ - taking_projection = is_kfunc(meta) && btf_is_projection_of(arg_tname, reg_tname); + taking_projection = is_kfunc(meta) && btf_is_kernel(reg_btf) && + !reg->var_off.value && + btf_is_projection_of(arg_tname, reg_tname); if (!taking_projection && !struct_same) { verbose(env, "%s %s expected pointer to %s %s but %s has a pointer to %s %s\n", meta->func_name, reg_arg_name(env, argno),