A global subprogram parameter tagged __arg_trusted is documented to
accept only the PTR_TRUSTED flavor of PTR_TO_BTF_ID, but the call-site
check 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. The trusted/RCU enforcement in
check_func_arg() is gated on is_kfunc(meta), which is false for a
subprogram call, so that block is skipped for __arg_trusted arguments.

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 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 | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index fd3c0206bd67d..fe5edbef85a16 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -9597,6 +9597,27 @@ static int check_func_arg(struct bpf_verifier_env *env, 
u32 arg, u32 slot, u32 p
                        }
                }
 
+               /* 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.
+                */
+               if ((arg_type & PTR_TRUSTED) && base_type(reg->type) == 
PTR_TO_BTF_ID &&
+                   !reg_is_referenced(env, reg)) {
+                       u32 flags = type_flag(reg->type);
+
+                       if (!(flags & BPF_REG_TRUSTED_MODIFIERS) ||
+                           (flags & ~(BPF_REG_TRUSTED_MODIFIERS |
+                                      (arg_type & PTR_MAYBE_NULL)))) {
+                               verbose(env, "%s must be referenced or 
trusted\n",
+                                       reg_arg_name(env, argno));
+                               return -EINVAL;
+                       }
+               }
+
                if (is_kfunc(meta) && (!is_trusted_reg(env, reg) ||
                                       
bpf_type_has_unsafe_modifiers(reg->type))) {
                        if (!(arg_type & MEM_RCU)) {

-- 
2.43.0


Reply via email to