Re: [PATCH bpf 1/2] bpf: Require referenced or trusted pointer for __arg_trusted arg
From: Amery Hung
Date: Mon Oct 05 2026 - 18:53:41 EST
On Mon, Oct 5, 2026 at 8:23 AM Yiyang Chen
<chenyy23@xxxxxxxxxxxxxxxxxxxxx> wrote:
>
> 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 <chenyy23@xxxxxxxxxxxxxxxxxxxxx>
> ---
> 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;
> + }
> + }
> +
Could we avoid adding a second provenance check and make the existing
kfunc check contract-driven instead?
Kfunc ARG_PTR_TO_BTF_ID arguments implicitly require trusted or
referenced provenance through the is_kfunc(meta) condition. Global
subprogs express the same requirement explicitly with PTR_TRUSTED.
Likewise, __nullable and __arg_nullable permit PTR_MAYBE_NULL,
which should not by itself make the provenance invalid.
First, encode the kfunc requirement in its generated prototype:
if (base_type(arg_type) == ARG_PTR_TO_BTF_ID) {
arg_type |= PTR_TRUSTED;
/* MEM_RCU denotes an accepted alternative provenance. */
if (is_kfunc_rcu(meta))
arg_type |= MEM_RCU;
}
The common check can then be driven entirely by the argument contract:
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)) {
/* must be referenced or trusted */
return -EINVAL;
}
if (!is_rcu_reg(reg)) {
/* must be an RCU pointer */
return -EINVAL;
}
}
is_trusted_reg_type() would contain the existing
is_trusted_reg() logic, but use the supplied normalized type for its
type and modifier checks while still using the original register ID for
reference lookup.
resolve_func_arg_type() should continue dropping PTR_TRUSTED when
it changes ARG_PTR_TO_BTF_ID into ARG_PTR_TO_MEM; that is a
different, fixed-size memory-buffer contract.
This also lets a kfunc __nullable argument accept
PTR_TRUSTED | PTR_MAYBE_NULL, as its existing contract specifies.
Please add corresponding kfunc coverage and adjust the claim that the
kfunc path is unchanged.
> if (is_kfunc(meta) && (!is_trusted_reg(env, reg) ||
> bpf_type_has_unsafe_modifiers(reg->type))) {
> if (!(arg_type & MEM_RCU)) {
>
> --
> 2.43.0
>
>