* [PATCH bpf 0/2] bpf: Require referenced or trusted pointer for __arg_trusted arg
@ 2026-10-05 15:12 Yiyang Chen
2026-10-05 15:12 ` [PATCH bpf 1/2] " Yiyang Chen
2026-10-05 15:12 ` [PATCH bpf 2/2] selftests/bpf: Cover non-trusted pointer to __arg_trusted subprog arg Yiyang Chen
0 siblings, 2 replies; 5+ messages in thread
From: Yiyang Chen @ 2026-10-05 15:12 UTC (permalink / raw)
To: Eduard Zingerman, Daniel Borkmann, Alexei Starovoitov,
Andrii Nakryiko, Kumar Kartikeya Dwivedi
Cc: Yonghong Song, Ihor Solodrai, Shuah Khan, Jiri Olsa,
linux-kselftest, linux-kernel, John Fastabend, Emil Tsalapatis,
bpf, Song Liu, Martin KaFai Lau
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 also
accepts a bare PTR_TO_BTF_ID and an MEM_RCU one.
check_reg_type() resolves the accepted types from the base argument type
alone without consulting arg_type's PTR_TRUSTED bit, and the trusted/RCU
enforcement in check_func_arg() is gated on is_kfunc(meta) so it never runs
for a subprogram call. A caller can therefore pass a pointer that is not
trusted and the callee is verified as holding PTR_TRUSTED, which makes the
dereference a raw load instead of a BPF_PROBE_MEM probe and lets the callee
pass the pointer 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 unsafe when __arg_nullable
declares it, so trusted-and-nullable arguments keep working. The kfunc path
is unchanged.
Yiyang Chen (2):
bpf: Require referenced or trusted pointer for __arg_trusted arg
selftests/bpf: Cover non-trusted pointer to __arg_trusted subprog arg
kernel/bpf/verifier.c | 21 ++++++++++++
.../selftests/bpf/progs/verifier_global_ptr_args.c | 40 ++++++++++++++++++++++
2 files changed, 61 insertions(+)
base-commit: d82cbceca49252bb0cd695326af8206c734e2744
--
2.34.0
^ permalink raw reply [flat|nested] 5+ messages in thread* [PATCH bpf 1/2] bpf: Require referenced or trusted pointer for __arg_trusted arg 2026-10-05 15:12 [PATCH bpf 0/2] bpf: Require referenced or trusted pointer for __arg_trusted arg Yiyang Chen @ 2026-10-05 15:12 ` Yiyang Chen 2026-10-05 15:26 ` sashiko-bot 2026-10-05 22:50 ` Amery Hung 2026-10-05 15:12 ` [PATCH bpf 2/2] selftests/bpf: Cover non-trusted pointer to __arg_trusted subprog arg Yiyang Chen 1 sibling, 2 replies; 5+ messages in thread From: Yiyang Chen @ 2026-10-05 15:12 UTC (permalink / raw) To: Eduard Zingerman, Daniel Borkmann, Alexei Starovoitov, Andrii Nakryiko, Kumar Kartikeya Dwivedi Cc: Yonghong Song, Ihor Solodrai, Shuah Khan, Jiri Olsa, linux-kselftest, linux-kernel, John Fastabend, Emil Tsalapatis, bpf, Song Liu, Martin KaFai Lau 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@mails.tsinghua.edu.cn> --- 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 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH bpf 1/2] bpf: Require referenced or trusted pointer for __arg_trusted arg 2026-10-05 15:12 ` [PATCH bpf 1/2] " Yiyang Chen @ 2026-10-05 15:26 ` sashiko-bot 2026-10-05 22:50 ` Amery Hung 1 sibling, 0 replies; 5+ messages in thread From: sashiko-bot @ 2026-10-05 15:26 UTC (permalink / raw) To: Yiyang Chen; +Cc: bpf Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Low] bpf: wrong multi-line comment style in check_func_arg() -- commit 66de311fe0f463c286064b3e009d6635674afd83 Author: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn> bpf: Require referenced or trusted pointer for __arg_trusted arg This commit modifies the verifier to reject PTR_TO_BTF_ID pointers that are neither referenced nor trusted for arguments marked PTR_TRUSTED. It fixes an issue where a bare PTR_TO_BTF_ID or MEM_RCU could bypass checks in subprogram calls. > 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 [Severity: Low] This isn't a bug, but does this violate the BPF subsystem multi-line comment style? The BPF guideline requires the opening /* to be on its own line for multi-line comments. > + * 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)) { -- Sashiko AI review · https://sashiko.dev/#/patchset/20261005-a3-arg-trusted-v4-v1-0-50ee0268fd39@mails.tsinghua.edu.cn?part=1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH bpf 1/2] bpf: Require referenced or trusted pointer for __arg_trusted arg 2026-10-05 15:12 ` [PATCH bpf 1/2] " Yiyang Chen 2026-10-05 15:26 ` sashiko-bot @ 2026-10-05 22:50 ` Amery Hung 1 sibling, 0 replies; 5+ messages in thread From: Amery Hung @ 2026-10-05 22:50 UTC (permalink / raw) To: Yiyang Chen Cc: Eduard Zingerman, Daniel Borkmann, Alexei Starovoitov, Andrii Nakryiko, Kumar Kartikeya Dwivedi, Yonghong Song, Ihor Solodrai, Shuah Khan, Jiri Olsa, linux-kselftest, linux-kernel, John Fastabend, Emil Tsalapatis, bpf, Song Liu, Martin KaFai Lau On Mon, Oct 5, 2026 at 8:23 AM Yiyang Chen <chenyy23@mails.tsinghua.edu.cn> 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@mails.tsinghua.edu.cn> > --- > 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 > > ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH bpf 2/2] selftests/bpf: Cover non-trusted pointer to __arg_trusted subprog arg 2026-10-05 15:12 [PATCH bpf 0/2] bpf: Require referenced or trusted pointer for __arg_trusted arg Yiyang Chen 2026-10-05 15:12 ` [PATCH bpf 1/2] " Yiyang Chen @ 2026-10-05 15:12 ` Yiyang Chen 1 sibling, 0 replies; 5+ messages in thread From: Yiyang Chen @ 2026-10-05 15:12 UTC (permalink / raw) To: Eduard Zingerman, Daniel Borkmann, Alexei Starovoitov, Andrii Nakryiko, Kumar Kartikeya Dwivedi Cc: Yonghong Song, Ihor Solodrai, Shuah Khan, Jiri Olsa, linux-kselftest, linux-kernel, John Fastabend, Emil Tsalapatis, bpf, Song Liu, Martin KaFai Lau verifier_global_ptr_args.c already covers passing an untrusted pointer to a __arg_trusted argument, which is rejected by the register type match. It does not cover the bare PTR_TO_BTF_ID or MEM_RCU flavors, both of which the type match accepts. Add two cases. The first walks task_struct->last_wakee out of a trusted current task and passes the result to a __arg_trusted subprogram parameter; the field has no __rcu tag and is not in BTF_TYPE_SAFE_RCU(task_struct), so the load yields a bare PTR_TO_BTF_ID. The second passes task_struct->real_parent, which is __rcu and on BTF_TYPE_SAFE_RCU(task_struct), so the load yields PTR_TO_BTF_ID | MEM_RCU. Both calls are expected to be rejected with "must be referenced or trusted". Signed-off-by: Yiyang Chen <chenyy23@mails.tsinghua.edu.cn> --- .../selftests/bpf/progs/verifier_global_ptr_args.c | 40 ++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c index 03507eeae3cb3..ec042f91bcf20 100644 --- a/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c +++ b/tools/testing/selftests/bpf/progs/verifier_global_ptr_args.c @@ -289,6 +289,46 @@ __weak int subprog_void_untrusted(void *p __arg_untrusted) return *(int *)p; } +__weak int subprog_trusted_bare(struct task_struct *task __arg_trusted) +{ + return task->pid; +} + +SEC("tp_btf/task_newtask") +__failure +__msg("R1 must be referenced or trusted") +__msg("Caller passes invalid args into func#{{.*}} ('subprog_trusted_bare')") +int bare_to_trusted(void *ctx) +{ + struct task_struct *cur = bpf_get_current_task_btf(); + struct task_struct *wakee; + + if (!cur) + return 0; + wakee = cur->last_wakee; + if (!wakee) + return 0; + return subprog_trusted_bare(wakee); +} + +/* + * real_parent is __rcu and on BTF_TYPE_SAFE_RCU(task_struct), so the load + * yields PTR_TO_BTF_ID | MEM_RCU. That is neither referenced nor trusted and + * must not satisfy __arg_trusted. + */ +SEC("tp_btf/task_newtask") +__failure +__msg("R1 must be referenced or trusted") +__msg("Caller passes invalid args into func#{{.*}} ('subprog_trusted_task_nullable')") +int memrcu_to_trusted(void *ctx) +{ + struct task_struct *cur = bpf_get_current_task_btf(); + + if (!cur) + return 0; + return subprog_trusted_task_nullable(cur->real_parent); +} + __weak int subprog_char_untrusted(char *p __arg_untrusted) { return *(int *)p; -- 2.43.0 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2026-10-05 22:51 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-10-05 15:12 [PATCH bpf 0/2] bpf: Require referenced or trusted pointer for __arg_trusted arg Yiyang Chen 2026-10-05 15:12 ` [PATCH bpf 1/2] " Yiyang Chen 2026-10-05 15:26 ` sashiko-bot 2026-10-05 22:50 ` Amery Hung 2026-10-05 15:12 ` [PATCH bpf 2/2] selftests/bpf: Cover non-trusted pointer to __arg_trusted subprog arg Yiyang Chen
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox