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 <[email protected]>
Signed-off-by: Yiyang Chen <[email protected]>
---
 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 353bde9ae2274..a0ab793f041d8 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -5327,15 +5327,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
@@ -5347,8 +5349,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)
@@ -9578,6 +9586,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;
@@ -9605,8 +9614,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;
 
@@ -13671,6 +13682,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

-- 
2.43.0


Reply via email to