bpf_iter_css_next() leaves kit->pos NULL at the end of the walk, but kit->start still points at the starting css. css_next_descendant_pre(), css_next_descendant_post(), css_next_child() and the ancestors walk all treat a NULL position as "start from @start", so a next() call after exhaustion returns elements again. The verifier relies on the iterator contract that once next() returns NULL it keeps returning NULL: process_iter_next_call() marks the iterator DRAINED and treats later next() results as the constant zero. A restarted css iteration therefore hands the BPF program a kernel pointer while the verifier believes R0 == 0. Clear kit->start when the position becomes NULL. bpf_iter_css_destroy() is a no-op, so no cleanup depends on @start after exhaustion. Fixes: 7251d0905e75 ("bpf: Introduce css open-coded iterator kfuncs") Signed-off-by: Kang Chen --- v2: - Shorten the code comment; move the rationale to the commit message. kernel/bpf/cgroup_iter.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/bpf/cgroup_iter.c b/kernel/bpf/cgroup_iter.c index fd51fe3d92cc..9e466ef2c433 100644 --- a/kernel/bpf/cgroup_iter.c +++ b/kernel/bpf/cgroup_iter.c @@ -365,6 +365,10 @@ __bpf_kfunc struct cgroup_subsys_state *bpf_iter_css_next(struct bpf_iter_css *i kit->pos = kit->pos ? kit->pos->parent : kit->start; } + /* Once NULL is returned, next() has to keep returning NULL. */ + if (!kit->pos) + kit->start = NULL; + return kit->pos; } -- 2.34.1