diff options
| author | Eduard Zingerman <eddyz87@gmail.com> | 2026-08-11 14:57:01 -0700 |
|---|---|---|
| committer | Eduard Zingerman <eddyz87@gmail.com> | 2026-08-11 14:57:01 -0700 |
| commit | 07cb86aa50816b070b99c89bf948762ef035a1f2 (patch) | |
| tree | c285cc5bc0c4989a39ef4ec92a49fe4da548f469 | |
| parent | d114bb98936770c501c958bf2bc5fb6b7c0bad7b (diff) | |
| parent | 81f209d5f7435646df047400a88bc81e0a16b9eb (diff) | |
Merge branch 'bpf-compare-iterator-types-during-state-pruning'
Ning Ding says:
====================
bpf: Compare iterator types during state pruning
Iterator stack slots can be marked MEM_RCU or PTR_UNTRUSTED. The
STACK_ITER check in stacksafe() does not compare this type, so state
pruning can treat these states as equal and prune an unsafe path.
Compare the type and add a test where RCU protection has a gap.
---
Changes in v2:
- Convert the regression test to inline assembly so its verifier-sensitive
control-flow layout is stable.
- Add Eduard Zingerman's Acked-by tag to patch 1.
v1: https://lore.kernel.org/bpf/20260807004320.134069-1-dingning04@gmail.com/
====================
Link: https://patch.msgid.link/20260811035955.132989-1-dingning04@gmail.com
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
| -rw-r--r-- | kernel/bpf/states.c | 3 | ||||
| -rw-r--r-- | tools/testing/selftests/bpf/progs/iters_task_failure.c | 46 |
2 files changed, 48 insertions, 1 deletions
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 */ 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 @@ -62,6 +62,52 @@ int BPF_PROG(iter_tasks_lock_and_unlock) } 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) { |
