From c3a0bfddaee4e1e5fce7f4426004669c017cd8f1 Mon Sep 17 00:00:00 2001 From: Yiyang Chen Date: Wed, 7 Oct 2026 16:25:29 +0000 Subject: bpf: Enforce trusted BTF ID argument provenance A global subprogram parameter tagged __arg_trusted is verified with PTR_TRUSTED, but the common call-site type check accepts bare PTR_TO_BTF_ID and MEM_RCU registers. The callee can consequently pass a pointer with weaker provenance to a kfunc that does not accept RCU-protected pointers. RCU protection only guarantees that the object remains allocated through a grace period. It does not guarantee that an object's refcount is nonzero, so an RCU-protected pointer cannot satisfy the trusted-pointer contract. For example, an RCU-protected bpf_cpumask loaded from a map may have a zero refcount. Passing it through __arg_trusted lets the callee pass it to bpf_cpumask_acquire(), whose plain refcount_inc() can retain an object that will be freed after the grace period. Make the existing provenance check contract-driven. Mark generated BTF ID kfunc argument types with PTR_TRUSTED, and add MEM_RCU only for KF_RCU kfuncs. Normalize PTR_MAYBE_NULL when the argument contract permits it. Then use the argument flags to require trusted or referenced provenance and allow MEM_RCU only when the contract says so. Split is_trusted_reg() so the normalized type is used for type and modifier checks while reference lookup still uses the original register. Conversion of scalar-struct BTF ID arguments to fixed-size memory continues to construct a new ARG_PTR_TO_MEM type without PTR_TRUSTED. Fixes: e2b3c4ff5d18 ("bpf: add __arg_trusted global func arg tag") Suggested-by: Amery Hung Signed-off-by: Yiyang Chen Reviewed-by: Amery Hung Link: https://lore.kernel.org/bpf/20261007-a3-arg-trusted-v4-v4-1-253ef3a1a69a@mails.tsinghua.edu.cn Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/verifier.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 74e5ac248a10..5958425e9a2a 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -5501,15 +5501,17 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_MAX] = { [CONST_PTR_TO_MAP] = btf_bpf_map_id, }; -static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_state *reg) +static bool is_trusted_reg_type(struct bpf_verifier_env *env, + const struct bpf_reg_state *reg, + enum bpf_reg_type type) { /* A referenced register is always trusted. */ if (reg_is_referenced(env, reg)) return true; /* Types listed in the reg2btf_ids are always trusted */ - if (reg2btf_ids[base_type(reg->type)] && - !bpf_type_has_unsafe_modifiers(reg->type)) + if (reg2btf_ids[base_type(type)] && + !bpf_type_has_unsafe_modifiers(type)) return true; /* If a register is not referenced, it is trusted if it has the @@ -5521,8 +5523,14 @@ static bool is_trusted_reg(struct bpf_verifier_env *env, const struct bpf_reg_st * Eventually, we should make PTR_TRUSTED the single source of truth * for whether a register is trusted. */ - return type_flag(reg->type) & BPF_REG_TRUSTED_MODIFIERS && - !bpf_type_has_unsafe_modifiers(reg->type); + return type_flag(type) & BPF_REG_TRUSTED_MODIFIERS && + !bpf_type_has_unsafe_modifiers(type); +} + +static bool is_trusted_reg(struct bpf_verifier_env *env, + const struct bpf_reg_state *reg) +{ + return is_trusted_reg_type(env, reg, reg->type); } static bool is_rcu_reg(const struct bpf_reg_state *reg) @@ -9798,6 +9806,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 p { const u32 *arg_btf_id = fn->arg_btf_id[arg]; const struct btf *arg_btf = is_kfunc(meta) ? meta->btf : btf_vmlinux; + enum bpf_reg_type reg_type; if (!meta->btf) { const struct bpf_reg_types *compatible; @@ -9825,8 +9834,10 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 p } } - if (is_kfunc(meta) && (!is_trusted_reg(env, reg) || - bpf_type_has_unsafe_modifiers(reg->type))) { + reg_type = reg->type & ~(arg_type & PTR_MAYBE_NULL); + if ((arg_type & PTR_TRUSTED) && + (!is_trusted_reg_type(env, reg, reg_type) || + bpf_type_has_unsafe_modifiers(reg_type))) { if (!(arg_type & MEM_RCU)) { const char *actual_type, *arg_name, *expected_type; @@ -14023,6 +14034,8 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, else proto->arg_btf_id[arg] = ref_id_ptr; + arg_type |= PTR_TRUSTED; + /* * A KF_RCU kfunc accepts an RCU-protected pointer where it would * otherwise demand a referenced or trusted one. Other argument kinds -- cgit v1.2.3