A global subprogram parameter tagged __arg_trusted is specified to
accept only the PTR_TRUSTED flavor of PTR_TO_BTF_ID, but the call-site
check in btf_check_func_arg_match() also accepts a bare PTR_TO_BTF_ID
and an MEM_RCU one.

check_reg_type() resolves the accepted set from the base argument type
alone and compares only MEM_RDONLY and PTR_MAYBE_NULL, so the
PTR_TRUSTED bit of arg_type is never consulted. btf_ptr_types lists
bare PTR_TO_BTF_ID and PTR_TO_BTF_ID | MEM_RCU alongside the trusted
flavor, and nothing else on the subprogram call path requires the
register to be referenced or trusted.

The callee is then validated with PTR_TRUSTED set on the register
while the caller passed a pointer that is neither referenced nor
trusted. bpf_may_fault_on_deref() is false for PTR_TRUSTED, so the
dereference becomes a raw load instead of a BPF_PROBE_MEM probe,
is_trusted_reg() kfuncs accept the pointer, and the callee can pass it
on to a kfunc that would have rejected it at the original call site.

Reject a PTR_TO_BTF_ID that is neither referenced nor trusted when the
argument is marked PTR_TRUSTED. A referenced register is accepted, as
in is_trusted_reg(). PTR_MAYBE_NULL is not counted as an unsafe
modifier when __arg_nullable declares it, so trusted-and-nullable
arguments keep working.

The check runs after check_reg_type() and check_func_arg_reg_off()
succeed so that type and offset diagnostics keep their current wording.
The kfunc path is unchanged.

Fixes: e2b3c4ff5d183da6d1863c2321413406a2752e7a ("bpf: add __arg_trusted global 
func arg tag")
Signed-off-by: Yiyang Chen <[email protected]>
---
 kernel/bpf/verifier.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 5f874979b8d75..c781e013c25cb 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9806,6 +9806,30 @@ static int btf_check_func_arg_match(struct 
bpf_verifier_env *env, int subprog,
                        err = err ?: check_func_arg_reg_off(env, reg, argno, 
arg->arg_type);
                        if (err)
                                return err;
+
+                       /* A __arg_trusted argument requires a referenced or 
trusted
+                        * pointer. btf_ptr_types also matches a bare 
PTR_TO_BTF_ID and
+                        * an MEM_RCU one, but neither is referenced or 
trusted, so the
+                        * callee would be verified with PTR_TRUSTED while the 
caller
+                        * passed something that is not. PTR_MAYBE_NULL is not 
counted
+                        * as unsafe when __arg_nullable declares it, because
+                        * bpf_type_has_unsafe_modifiers() treats that flag as 
unsafe.
+                        *
+                        * Checked after the type/offset match so that type and 
offset
+                        * diagnostics keep their current wording.
+                        */
+                       if (arg->arg_type & PTR_TRUSTED) {
+                               u32 flags = type_flag(reg->type);
+
+                               if (!reg_is_referenced(env, reg) &&
+                                   (!(flags & BPF_REG_TRUSTED_MODIFIERS) ||
+                                    (flags & ~(BPF_REG_TRUSTED_MODIFIERS |
+                                               (arg->arg_type & 
PTR_MAYBE_NULL))))) {
+                                       bpf_log(log, "%s must be referenced or 
trusted\n",
+                                               reg_arg_name(env, argno));
+                                       return -EINVAL;
+                               }
+                       }
                } else {
                        verifier_bug(env, "unrecognized %s type %d",
                                     reg_arg_name(env, argno), arg->arg_type);

-- 
2.43.0


Reply via email to