From 83608e303b95d07afba1c15da0b5d9e513c2f15a Mon Sep 17 00:00:00 2001 From: Ning Ding Date: Mon, 10 Aug 2026 20:59:54 -0700 Subject: [PATCH 1/2] bpf: Compare iterator types during state pruning An iterator stack slot can be MEM_RCU or PTR_UNTRUSTED. These states must not be equal, or the verifier can prune an unsafe path. Compare the pointer type for STACK_ITER slots. Fixes: dfab99df147b ("bpf: teach the verifier to enforce css_iter and task_iter in RCU CS") Signed-off-by: Ning Ding Acked-by: Eduard Zingerman Link: https://patch.msgid.link/20260811035955.132989-2-dingning04@gmail.com Signed-off-by: Eduard Zingerman --- kernel/bpf/states.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index ea2153cf28d0..4e6aafad33bd 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -812,7 +812,8 @@ static bool stacksafe(struct bpf_verifier_env *env, struct bpf_func_state *old, * infinite loop check triggering, see * iter_active_depths_differ() */ - if (old_reg->iter.btf != cur_reg->iter.btf || + if (old_reg->type != cur_reg->type || + old_reg->iter.btf != cur_reg->iter.btf || old_reg->iter.btf_id != cur_reg->iter.btf_id || old_reg->iter.state != cur_reg->iter.state || /* ignore {old_reg,cur_reg}->iter.depth, see above */ From 81f209d5f7435646df047400a88bc81e0a16b9eb Mon Sep 17 00:00:00 2001 From: Ning Ding Date: Mon, 10 Aug 2026 20:59:55 -0700 Subject: [PATCH 2/2] selftests/bpf: Test RCU iterator state pruning Add a path where RCU protection reaches zero and then starts again. The iterator is untrusted after this gap and must be rejected. Signed-off-by: Ning Ding Link: https://patch.msgid.link/20260811035955.132989-3-dingning04@gmail.com Signed-off-by: Eduard Zingerman --- .../selftests/bpf/progs/iters_task_failure.c | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/tools/testing/selftests/bpf/progs/iters_task_failure.c b/tools/testing/selftests/bpf/progs/iters_task_failure.c index fe3663dedbe1..566a1d3dffea 100644 --- a/tools/testing/selftests/bpf/progs/iters_task_failure.c +++ b/tools/testing/selftests/bpf/progs/iters_task_failure.c @@ -61,6 +61,52 @@ int BPF_PROG(iter_tasks_lock_and_unlock) return 0; } +SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") +__failure __msg("expected an RCU CS when using bpf_iter_task_next") +__flag(BPF_F_TEST_STATE_FREQ) +int BPF_PROG(iter_tasks_rcu_state_pruning) +{ + struct bpf_iter_task it; + + asm volatile ( + "call %[bpf_rcu_read_lock];" + "r1 = %[it];" + "r2 = 0;" + "r3 = 0;" /* BPF_TASK_ITER_ALL_PROCS */ + "call %[bpf_iter_task_new];" + + "call %[bpf_get_prandom_u32];" + "if w0 == 0 goto unprotected_%=;" + + /* Keep the outer RCU lock active on the straight-line path. */ + "call %[bpf_rcu_read_lock];" + "call %[bpf_rcu_read_unlock];" + "goto merge_%=;" + + "unprotected_%=:" + /* Create an unprotected gap on the taken path. */ + "call %[bpf_rcu_read_unlock];" + "call %[bpf_rcu_read_lock];" + + "merge_%=: r1 = %[it];" + "call %[bpf_iter_task_next];" + "r1 = %[it];" + "call %[bpf_iter_task_destroy];" + "call %[bpf_rcu_read_unlock];" + : + : __imm_ptr(it), + __imm(bpf_get_prandom_u32), + __imm(bpf_iter_task_new), + __imm(bpf_iter_task_next), + __imm(bpf_iter_task_destroy), + __imm(bpf_rcu_read_lock), + __imm(bpf_rcu_read_unlock) + : __clobber_common + ); + + return 0; +} + SEC("?fentry.s/" SYS_PREFIX "sys_getpgid") __failure __msg("expected an RCU CS when using bpf_iter_css_next") int BPF_PROG(iter_css_lock_and_unlock)