* [PATCH bpf-next v2 1/6] bpf: Carry MEM_RCU for trusted fields with MEM_RCU parents [not found] <20261020145059.2146170-1-utilityemal77@gmail.com> @ 2026-10-20 14:50 ` Justin Suess 2026-10-21 0:37 ` Alexei Starovoitov 2026-10-20 14:50 ` [PATCH bpf-next v2 3/6] bpf: Bind trusted fields to their parent's reference Justin Suess 2026-10-20 14:50 ` [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator Justin Suess 2 siblings, 1 reply; 7+ messages in thread From: Justin Suess @ 2026-10-20 14:50 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi Cc: Song Liu, Puranjay Mohan, bpf, Justin Suess, stable Substitute MEM_RCU | PTR_MAYBE_NULL instead of PTR_TRUSTED for fields declared as trusted when the parent pointer is MEM_RCU. Today, walking a field in the __safe_trusted or __safe_trusted_or_null set invariably yields PTR_TRUSTED if its parent is trusted. If the parent is instead RCU protected, the resulting pointer survives bpf_rcu_read_unlock() as fully trusted: it can be dereferenced without PROBE_MEM and passed to kfuncs expecting trusted arguments after the parent's RCU grace period, e.g. mm->exe_file (MEM_RCU) followed by file->f_inode escapes the RCU CS as a trusted inode. Limit the child field's lifetime to that of the parent. An RCU-protected parent also guarantees less about the field's value than a trusted one. __safe_trusted promises a non-NULL field for as long as the parent object is valid, but RCU only keeps the parent's memory valid: struct file is allocated SLAB_TYPESAFE_BY_RCU, so a file read under RCU can be freed and reused within the same CS. Mark the field PTR_MAYBE_NULL as well, as is already done for __rcu tagged fields, so programs check it before dereferencing. Fixes: 6fcd486b3a0a ("bpf: Refactor RCU enforcement in the verifier.") Cc: stable@vger.kernel.org Link: https://lore.kernel.org/bpf/sashiko-outbox-171590@kernel.org/ Signed-off-by: Justin Suess <utilityemal77@gmail.com> --- kernel/bpf/verifier.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 2f190abe3448..534200c0d27d 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6684,9 +6684,9 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, * trusted if we are in an RCU CS. Such pointer can be NULL. */ if (type_is_trusted(env, reg, field_name, btf_id)) { - flag |= PTR_TRUSTED; + flag |= is_rcu_reg(reg) ? MEM_RCU | PTR_MAYBE_NULL : PTR_TRUSTED; } else if (type_is_trusted_or_null(env, reg, field_name, btf_id)) { - flag |= PTR_TRUSTED | PTR_MAYBE_NULL; + flag |= (is_rcu_reg(reg) ? MEM_RCU : PTR_TRUSTED) | PTR_MAYBE_NULL; } else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) { if (type_is_rcu(env, reg, field_name, btf_id)) { /* ignore __rcu tag and mark it MEM_RCU */ -- 2.55.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v2 1/6] bpf: Carry MEM_RCU for trusted fields with MEM_RCU parents 2026-10-20 14:50 ` [PATCH bpf-next v2 1/6] bpf: Carry MEM_RCU for trusted fields with MEM_RCU parents Justin Suess @ 2026-10-21 0:37 ` Alexei Starovoitov 0 siblings, 0 replies; 7+ messages in thread From: Alexei Starovoitov @ 2026-10-21 0:37 UTC (permalink / raw) To: Justin Suess, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi Cc: Song Liu, Puranjay Mohan, bpf, stable On Tue, Oct 20, 2026 at 07:50 AM Justin Suess <utilityemal77@gmail.com> wrote: > An RCU-protected parent also guarantees less about the field's value > than a trusted one. __safe_trusted promises a non-NULL field for as > long as the parent object is valid, but RCU only keeps the parent's > memory valid: struct file is allocated SLAB_TYPESAFE_BY_RCU, so a file > read under RCU can be freed and reused within the same CS. It guarantees less about the field's lifetime too. A MEM_RCU parent only means that its memory is not freed yet, so the field is safe only if the object it points to is RCU freed itself. inode is. mm_struct is not, __mmdrop() does kmem_cache_free() without a grace period. With this patch the first MEM_RCU vma hands out vma->vm_mm as rcu_ptr_mm_struct. That's what the __safe_rcu_or_null list is for. Let's put f_inode there and not apply the trusted lists to a parent that is not trusted. exe_file->f_inode is the same rcu_ptr_or_null_inode, so the tests in patch 2 don't change, and the ternaries go away. Something like this (compile tested only): diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 01434e1..219d027 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6476,6 +6476,11 @@ BTF_TYPE_SAFE_RCU_OR_NULL(struct request_sock) { struct sock *sk; }; +/* struct file is SLAB_TYPESAFE_BY_RCU. A reused file has f_inode == NULL. */ +BTF_TYPE_SAFE_RCU_OR_NULL(struct file) { + struct inode *f_inode; +}; + /* full trusted: these fields are trusted even outside of RCU CS and never NULL */ BTF_TYPE_SAFE_TRUSTED(struct bpf_iter_meta) { struct seq_file *seq; @@ -6552,6 +6557,7 @@ static bool type_is_rcu_or_null(struct bpf_verifier_env *env, BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU_OR_NULL(struct mm_struct)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU_OR_NULL(struct sk_buff)); BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU_OR_NULL(struct request_sock)); + BTF_TYPE_EMIT(BTF_TYPE_SAFE_RCU_OR_NULL(struct file)); return btf_nested_type_is_trusted(&env->log, reg, field_name, btf_id, "__safe_rcu_or_null"); } @@ -6757,10 +6763,14 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, * * A regular RCU-protected pointer with __rcu tag can also be deemed * trusted if we are in an RCU CS. Such pointer can be NULL. + * + * If the parent is not trusted, the field can't regain trusted status. */ - if (type_is_trusted(env, reg, field_name, btf_id)) { + bool trusted = is_trusted_reg(env, reg); + + if (trusted && type_is_trusted(env, reg, field_name, btf_id)) { flag |= PTR_TRUSTED; - } else if (type_is_trusted_or_null(env, reg, field_name, btf_id)) { + } else if (trusted && type_is_trusted_or_null(env, reg, field_name, btf_id)) { flag |= PTR_TRUSTED | PTR_MAYBE_NULL; } else if (in_rcu_cs(env) && !type_may_be_null(reg->type)) { if (type_is_rcu(env, reg, field_name, btf_id)) { [...] > Cc: stable@vger.kernel.org That shouldn't go to stable. pw-bot: cr ^ permalink raw reply related [flat|nested] 7+ messages in thread
* [PATCH bpf-next v2 3/6] bpf: Bind trusted fields to their parent's reference [not found] <20261020145059.2146170-1-utilityemal77@gmail.com> 2026-10-20 14:50 ` [PATCH bpf-next v2 1/6] bpf: Carry MEM_RCU for trusted fields with MEM_RCU parents Justin Suess @ 2026-10-20 14:50 ` Justin Suess 2026-10-21 1:16 ` Kumar Kartikeya Dwivedi 2026-10-20 14:50 ` [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator Justin Suess 2 siblings, 1 reply; 7+ messages in thread From: Justin Suess @ 2026-10-20 14:50 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi Cc: Song Liu, Puranjay Mohan, bpf, Justin Suess, stable A field in the __safe_trusted or __safe_trusted_or_null set walked out of a referenced parent yields PTR_TRUSTED with nothing tying it to the reference that keeps it alive. Releasing the parent invalidates the parent's own register but not the walked field, which stays fully trusted after the object may already be freed: file = bpf_get_task_exe_file(task); /* acquired */ inode = file->f_inode; /* __safe_trusted */ bpf_put_file(file); /* inode stays trusted */ Set the walked field's parent_id to the parent's reference id, or to the parent's own parent_id so a chain of derived fields stays bound to the original reference. release_reference() already invalidates every register whose parent_id matches, recursively, and regsafe() already maps parent_id through check_ids(), so no other verifier changes are needed. An RCU-protected parent keeps bounding its fields to the RCU critical section through MEM_RCU instead. Fixes: 6fcd486b3a0a ("bpf: Refactor RCU enforcement in the verifier.") Cc: stable@vger.kernel.org Signed-off-by: Justin Suess <utilityemal77@gmail.com> --- kernel/bpf/verifier.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 534200c0d27d..c85c83a6ee63 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -6562,6 +6562,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, const char *tname = btf_name_by_offset(reg->btf, t->name_off); const char *field_name = NULL; enum bpf_type_flag flag = 0; + u32 parent_id = 0; u32 btf_id = 0; s64 min_off; int ret; @@ -6716,6 +6717,11 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, */ flag = PTR_UNTRUSTED; } + + /* A trusted field is pinned by whatever pins its parent. */ + if (flag & PTR_TRUSTED) + parent_id = reg_is_referenced(env, reg) ? reg->id : + reg->parent_id; } else { /* Old compat. Deprecated */ clear_trusted_flags(&flag); @@ -6725,6 +6731,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, ret = mark_btf_ld_reg(env, regs, value_regno, ret, reg->btf, btf_id, flag); if (ret < 0) return ret; + regs[value_regno].parent_id = parent_id; } return 0; -- 2.55.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v2 3/6] bpf: Bind trusted fields to their parent's reference 2026-10-20 14:50 ` [PATCH bpf-next v2 3/6] bpf: Bind trusted fields to their parent's reference Justin Suess @ 2026-10-21 1:16 ` Kumar Kartikeya Dwivedi 0 siblings, 0 replies; 7+ messages in thread From: Kumar Kartikeya Dwivedi @ 2026-10-21 1:16 UTC (permalink / raw) To: Justin Suess Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Song Liu, Puranjay Mohan, bpf, stable On Tue, 20 Oct 2026 at 16:56, Justin Suess <utilityemal77@gmail.com> wrote: > > A field in the __safe_trusted or __safe_trusted_or_null set walked out > of a referenced parent yields PTR_TRUSTED with nothing tying it to the > reference that keeps it alive. Releasing the parent invalidates the > parent's own register but not the walked field, which stays fully > trusted after the object may already be freed: > > file = bpf_get_task_exe_file(task); /* acquired */ > inode = file->f_inode; /* __safe_trusted */ > bpf_put_file(file); /* inode stays trusted */ > > Set the walked field's parent_id to the parent's reference id, or to > the parent's own parent_id so a chain of derived fields stays bound to > the original reference. release_reference() already invalidates every > register whose parent_id matches, recursively, and regsafe() already > maps parent_id through check_ids(), so no other verifier changes are > needed. An RCU-protected parent keeps bounding its fields to the RCU > critical section through MEM_RCU instead. > > Fixes: 6fcd486b3a0a ("bpf: Refactor RCU enforcement in the verifier.") > Cc: stable@vger.kernel.org > Signed-off-by: Justin Suess <utilityemal77@gmail.com> > --- > kernel/bpf/verifier.c | 7 +++++++ > 1 file changed, 7 insertions(+) > > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 534200c0d27d..c85c83a6ee63 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -6562,6 +6562,7 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, > const char *tname = btf_name_by_offset(reg->btf, t->name_off); > const char *field_name = NULL; > enum bpf_type_flag flag = 0; > + u32 parent_id = 0; > u32 btf_id = 0; > s64 min_off; > int ret; > @@ -6716,6 +6717,11 @@ static int check_ptr_to_btf_access(struct bpf_verifier_env *env, > */ > flag = PTR_UNTRUSTED; > } > + > + /* A trusted field is pinned by whatever pins its parent. */ > + if (flag & PTR_TRUSTED) > + parent_id = reg_is_referenced(env, reg) ? reg->id : > + reg->parent_id; This bit looks correct. While you are at it, there is also another related case that needs fixing. file = bpf_get_task_exe_file(task); inode = bpf_real_data_inode(file); bpf_put_file(file); mode = inode->i_mode; still loads with this series applied. bpf_real_data_inode() is neither KF_ACQUIRE nor KF_RCU_PROTECTED, so check_kfunc_call() types R0 as PTR_TRUSTED with program lifetime and nothing ties it to the file: 6: (85) call bpf_real_data_inode#65472 ; R0=trusted_ptr_or_null_inode(id=3) refs=2 9: (85) call bpf_put_file#65416 10: (15) if r7 == 0x0 goto pc+3 ; R7=trusted_ptr_inode(id=3) 11: (69) r1 = *(u16 *)(r7 +0) The verifier cannot infer that relationship, the kfunc has to declare it. Current proposal is to add a __derives_ret parameter name suffix, in the same style as __sz/__str/__uninit: __bpf_kfunc struct inode *bpf_real_data_inode(struct file *file__derives_ret) At registration (btf_check_kfunc_protos()): at most one argument carries the suffix, that argument is a pointer, the kfunc returns a pointer, and the flags do not include KF_ACQUIRE or KF_RELEASE (an acquire returns its own reference, a release has nothing left to borrow from). At verification: in check_func_args(), when the argument carries the suffix, record the lifetime id of the register passed for it, which is its own id if it is referenced and its parent_id otherwise. That is the ternary you have in check_ptr_to_btf_access(); factor it into a reg_lifetime_id() helper and use it in both places. In check_kfunc_call(), after R0 has been typed, set R0's parent_id to that id for pointer returns. On the PTR_TO_MEM return path, skip the implicit ref_obj inference when the kfunc is annotated so the explicit annotation wins. A program-lifetime argument has lifetime id 0, so the return keeps today's behavior, and a trusted field walked out of the returned pointer inherits the same parent_id through your 3/6 patch. Then annotate bpf_real_data_inode() and bpf_kfunc_call_test3() in bpf_testmod (it returns its argument), add a section to kfuncs.rst, and add reject/accept tests for the real_data_inode case next to yours. One known limit: if a KF_RCU kfunc ever borrows from an MEM_RCU argument, the return should carry MEM_RCU rather than PTR_TRUSTED. No such kfunc exists today, so this does not handle it yet. It might be better to future proof the handling for this so that we don't run into similar bug later on. Also, the last sentence of the commit log about RCU parents belongs with 1/6, where Alexei suggested the __safe_rcu_or_null route. ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator [not found] <20261020145059.2146170-1-utilityemal77@gmail.com> 2026-10-20 14:50 ` [PATCH bpf-next v2 1/6] bpf: Carry MEM_RCU for trusted fields with MEM_RCU parents Justin Suess 2026-10-20 14:50 ` [PATCH bpf-next v2 3/6] bpf: Bind trusted fields to their parent's reference Justin Suess @ 2026-10-20 14:50 ` Justin Suess 2026-10-20 15:25 ` bot+bpf-ci 2026-10-21 0:57 ` Kumar Kartikeya Dwivedi 2 siblings, 2 replies; 7+ messages in thread From: Justin Suess @ 2026-10-20 14:50 UTC (permalink / raw) To: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Kumar Kartikeya Dwivedi Cc: Song Liu, Puranjay Mohan, bpf, Justin Suess, stable An element returned by an iterator's next() kfunc is kept alive by the iterator alone. bpf_iter_task_vma_next() returns a snapshot that lives in the iterator's own allocation and holds the vma's vm_file reference only until the next step or destroy; bpf_iter_kmem_cache_next() and bpf_iter_dmabuf_next() hand out objects whose reference the iterator drops the same way. The verifier marks every such element PTR_TRUSTED with nothing tying it to the iterator, so a pointer saved across bpf_iter_<type>_destroy() stays fully trusted after the memory or the reference behind it is gone: bpf_iter_task_vma_new(&it, task, 0); vma = bpf_iter_task_vma_next(&it); file = vma->vm_file; /* __safe_trusted_or_null */ bpf_iter_task_vma_destroy(&it); /* frees snapshot, fput() */ start = vma->vm_start; /* vma, file still trusted */ Set the element's parent_id to the iterator's reference id, as is already done for the PTR_TO_MEM elements of bpf_iter_num_next(), so that destroying the iterator invalidates the element together with every trusted field acquired from it. Elements of an RCU-protected iterator keep MEM_RCU and stay bounded by the critical section instead: they point at RCU-managed objects rather than iterator state. Fixes: 4ac454682158 ("bpf: Introduce task_vma open-coded iterator kfuncs") Cc: stable@vger.kernel.org Signed-off-by: Justin Suess <utilityemal77@gmail.com> --- Documentation/bpf/bpf_iterators.rst | 6 +++++- kernel/bpf/verifier.c | 13 +++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Documentation/bpf/bpf_iterators.rst b/Documentation/bpf/bpf_iterators.rst index 189e3ec1c6c8..08c9c15b59ac 100644 --- a/Documentation/bpf/bpf_iterators.rst +++ b/Documentation/bpf/bpf_iterators.rst @@ -79,7 +79,11 @@ Constructor/next/destructor implementation contract is as follows: will eventually return NULL when elements are exhausted. Once NULL is returned, subsequent next calls *should keep returning NULL*. Next method is marked with KF_ITER_NEXT (and should also have KF_RET_NULL as - NULL-returning kfunc, of course). + NULL-returning kfunc, of course). An element is only guaranteed to be + valid until the iterator is destroyed, so the verifier invalidates it, + and every trusted pointer derived from it, at `bpf_iter_<type>_destroy()`. + Elements of an RCU-protected iterator (see KF_RCU_PROTECTED) are instead + valid until the end of the RCU critical section. - destructor, `bpf_iter_<type>_destroy()`, is always called once. Even if constructor failed or next returned nothing. Destructor frees up any diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index c85c83a6ee63..9ce5517dc1a0 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -15504,6 +15504,19 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, regs[BPF_REG_0].btf = desc_btf; regs[BPF_REG_0].type = type; regs[BPF_REG_0].btf_id = ptr_type_id; + + /* + * A trusted element is pinned by the iterator that + * produced it and dies with it, like the memory + * returned by bpf_iter_num_next() above. An RCU + * element is bounded by the critical section instead. + */ + if (bpf_is_iter_next_kfunc(&meta) && (type & PTR_TRUSTED)) { + err = validate_ref_obj(env, &meta.ref_obj); + if (err) + return err; + regs[BPF_REG_0].parent_id = meta.ref_obj.id; + } } if (is_kfunc_ret_null(&meta)) { -- 2.55.0 ^ permalink raw reply related [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator 2026-10-20 14:50 ` [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator Justin Suess @ 2026-10-20 15:25 ` bot+bpf-ci 2026-10-21 0:57 ` Kumar Kartikeya Dwivedi 1 sibling, 0 replies; 7+ messages in thread From: bot+bpf-ci @ 2026-10-20 15:25 UTC (permalink / raw) To: utilityemal77, ast, daniel, andrii, eddyz87, memxor Cc: song, puranjay, bpf, utilityemal77, stable, ast, andrii, daniel, martin.lau, eddyz87, yonghong.song, mason, ihor.solodrai [-- Attachment #1: Type: text/plain, Size: 7228 bytes --] > bpf: Bind iterator elements to their iterator TL;DR: - Iterator elements may stay trusted after the next next() call drops them, so the verifier may still accept use-after-free programs - Fixes: tag likely should be 4cc8c50c9abc > bpf: Bind iterator elements to their iterator > > An element returned by an iterator's next() kfunc is kept alive by the > iterator alone. bpf_iter_task_vma_next() returns a snapshot that lives > in the iterator's own allocation and holds the vma's vm_file reference > only until the next step or destroy; bpf_iter_kmem_cache_next() and > bpf_iter_dmabuf_next() hand out objects whose reference the iterator > drops the same way. The verifier marks every such element PTR_TRUSTED > with nothing tying it to the iterator, so a pointer saved across > bpf_iter_<type>_destroy() stays fully trusted after the memory or the > reference behind it is gone: > > bpf_iter_task_vma_new(&it, task, 0); > vma = bpf_iter_task_vma_next(&it); > file = vma->vm_file; /* __safe_trusted_or_null */ > bpf_iter_task_vma_destroy(&it); /* frees snapshot, fput() */ > start = vma->vm_start; /* vma, file still trusted */ > > Set the element's parent_id to the iterator's reference id, as is > already done for the PTR_TO_MEM elements of bpf_iter_num_next(), so > that destroying the iterator invalidates the element together with > every trusted field acquired from it. Elements of an RCU-protected > iterator keep MEM_RCU and stay bounded by the critical section instead: > they point at RCU-managed objects rather than iterator state. > > Fixes: 4ac454682158 ("bpf: Introduce task_vma open-coded iterator kfuncs") > Cc: stable@vger.kernel.org > Signed-off-by: Justin Suess <utilityemal77@gmail.com> The Fixes: tag points at 4ac454682158 ("bpf: Introduce task_vma open-coded iterator kfuncs"). Did the problem described here exist at that commit? At 4ac454682158, check_kfunc_call() gave every kfunc-returned struct pointer, including the result of bpf_iter_task_vma_next(), a plain PTR_TO_BTF_ID without PTR_TRUSTED, so there was no trusted pointer to outlive the iterator. The PTR_TRUSTED marking of iter next() results (or MEM_RCU for KF_RCU_PROTECTED iterators) came with 4cc8c50c9abc ("bpf: Make the pointer returned by iter next method valid"), and that change affects all three iterators named in the commit message. The kmem_cache (2e9a548009c2) and dmabuf (6eab7ac7c5ee) iterators were added after it, and task_vma only became affected through it. The task_vma snapshot and vm_file reference described in the commit message came later with 4cbee026db54 ("bpf: return VMA snapshot from task_vma iterator"), which itself carries a Fixes: tag pointing at 4ac454682158. Should this be: Fixes: 4cc8c50c9abc ("bpf: Make the pointer returned by iter next method valid") > diff --git a/Documentation/bpf/bpf_iterators.rst b/Documentation/bpf/bpf_iterators.rst > index 189e3ec1c6c8..08c9c15b59ac 100644 > --- a/Documentation/bpf/bpf_iterators.rst > +++ b/Documentation/bpf/bpf_iterators.rst > @@ -79,7 +79,11 @@ Constructor/next/destructor implementation contract is as follows: [ ... ] > - NULL-returning kfunc, of course). > + NULL-returning kfunc, of course). An element is only guaranteed to be > + valid until the iterator is destroyed, so the verifier invalidates it, > + and every trusted pointer derived from it, at `bpf_iter_<type>_destroy()`. > + Elements of an RCU-protected iterator (see KF_RCU_PROTECTED) are instead > + valid until the end of the RCU critical section. [ ... ] > diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c > index 5d3deea1cdc7..869e8d4083c6 100644 > --- a/kernel/bpf/verifier.c > +++ b/kernel/bpf/verifier.c > @@ -15762,6 +15762,19 @@ static int check_kfunc_call(struct bpf_verifier_env *env, struct bpf_insn *insn, > regs[BPF_REG_0].btf = desc_btf; > regs[BPF_REG_0].type = type; > regs[BPF_REG_0].btf_id = ptr_type_id; > + > + /* > + * A trusted element is pinned by the iterator that > + * produced it and dies with it, like the memory > + * returned by bpf_iter_num_next() above. An RCU > + * element is bounded by the critical section instead. > + */ > + if (bpf_is_iter_next_kfunc(&meta) && (type & PTR_TRUSTED)) { > + err = validate_ref_obj(env, &meta.ref_obj); > + if (err) > + return err; > + regs[BPF_REG_0].parent_id = meta.ref_obj.id; > + } > } Is this fix complete for the lifetime of the element? The documentation hunk above and the new comment say the element is valid until bpf_iter_<type>_destroy(), but several next() kfuncs drop the previous element inside the following call to next(), not only at destroy. The commit message says as much for task_vma: "holds the vma's vm_file reference only until the next step or destroy". What the next() kfuncs do to the previous element: bpf_iter_task_vma_next(): bpf_iter_task_vma_snapshot_reset() does fput(snap->vm_file) and then overwrites the snapshot. This runs before bpf_iter_task_vma_find_next(), so it also runs on the call that returns NULL. bpf_iter_dmabuf_next() -> dma_buf_iter_next(): dma_buf_put() on the previous buffer. bpf_iter_kmem_cache_next(): prev->refcount--, or kmem_cache_destroy(prev) if the refcount was 1. bpf_iter_css_task_next() -> css_task_iter_next(): put_task_struct(it->cur_task). With this patch the element, and the trusted fields walked from it, keep parent_id equal to the iterator id and stay PTR_TRUSTED until destroy. Would a program like this still verify? bpf_iter_task_vma_new(&it, task, 0); while ((vma = bpf_iter_task_vma_next(&it))) if (vma->vm_file) file = vma->vm_file; /* trusted, parent_id = iter */ /* the last next() returned NULL after fput(file) */ if (file) ret = subprog_trusted_file(file); /* still trusted */ bpf_iter_task_vma_destroy(&it); Once bpf_iter_task_vma_next() has called vma_end_read(), nothing stops the VMA from being unmapped concurrently. Can the file then lose its last reference while the program still passes it to a trusted-argument kfunc? The kmem_cache and dmabuf cases look similar: an element saved from iteration N stays trusted in iteration N+1 after the iterator has put it. A later patch in the series, a51a1f92e627 ("selftests/bpf: Test iterator element invalidation on destroy"), covers only the destroy case, and nothing in the series invalidates the descendants of the iterator id at next(). Should the verifier invalidate the registers whose parent_id is the iterator id at each next() call for PTR_TRUSTED elements? If not, should the documentation, the comment and the commit message say that elements stop being valid at the next step rather than at destroy? This looks like a remaining gap in the fix rather than something this patch introduces. --- AI reviewed your patch. Please fix the bug or email reply why it's not a bug. See: https://github.com/kernel-patches/vmtest/blob/master/ci/claude/README.md CI run summary: https://github.com/kernel-patches/bpf/actions/runs/39445554665 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator 2026-10-20 14:50 ` [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator Justin Suess 2026-10-20 15:25 ` bot+bpf-ci @ 2026-10-21 0:57 ` Kumar Kartikeya Dwivedi 1 sibling, 0 replies; 7+ messages in thread From: Kumar Kartikeya Dwivedi @ 2026-10-21 0:57 UTC (permalink / raw) To: Justin Suess Cc: Alexei Starovoitov, Daniel Borkmann, Andrii Nakryiko, Eduard Zingerman, Song Liu, Puranjay Mohan, bpf, stable On Tue, 20 Oct 2026 at 16:56, Justin Suess <utilityemal77@gmail.com> wrote: > > An element returned by an iterator's next() kfunc is kept alive by the > iterator alone. bpf_iter_task_vma_next() returns a snapshot that lives > in the iterator's own allocation and holds the vma's vm_file reference > only until the next step or destroy; bpf_iter_kmem_cache_next() and > bpf_iter_dmabuf_next() hand out objects whose reference the iterator > drops the same way. The verifier marks every such element PTR_TRUSTED > with nothing tying it to the iterator, so a pointer saved across > bpf_iter_<type>_destroy() stays fully trusted after the memory or the > reference behind it is gone: > > bpf_iter_task_vma_new(&it, task, 0); > vma = bpf_iter_task_vma_next(&it); > file = vma->vm_file; /* __safe_trusted_or_null */ > bpf_iter_task_vma_destroy(&it); /* frees snapshot, fput() */ > start = vma->vm_start; /* vma, file still trusted */ > > Set the element's parent_id to the iterator's reference id, as is > already done for the PTR_TO_MEM elements of bpf_iter_num_next(), so > that destroying the iterator invalidates the element together with > every trusted field acquired from it. Elements of an RCU-protected > iterator keep MEM_RCU and stay bounded by the critical section instead: > they point at RCU-managed objects rather than iterator state. > > Fixes: 4ac454682158 ("bpf: Introduce task_vma open-coded iterator kfuncs") > Cc: stable@vger.kernel.org > Signed-off-by: Justin Suess <utilityemal77@gmail.com> > --- I would drop this one. Xu is trying to fix it here, which is probably the more proper fix: https://lore.kernel.org/bpf/20260927120406.1041915-1-xyx2021@mail.ustc.edu.cn ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-10-21 1:16 UTC | newest]
Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
[not found] <20261020145059.2146170-1-utilityemal77@gmail.com>
2026-10-20 14:50 ` [PATCH bpf-next v2 1/6] bpf: Carry MEM_RCU for trusted fields with MEM_RCU parents Justin Suess
2026-10-21 0:37 ` Alexei Starovoitov
2026-10-20 14:50 ` [PATCH bpf-next v2 3/6] bpf: Bind trusted fields to their parent's reference Justin Suess
2026-10-21 1:16 ` Kumar Kartikeya Dwivedi
2026-10-20 14:50 ` [PATCH bpf-next v2 5/6] bpf: Bind iterator elements to their iterator Justin Suess
2026-10-20 15:25 ` bot+bpf-ci
2026-10-21 0:57 ` Kumar Kartikeya Dwivedi
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox