diff options
| author | Eduard Zingerman <eddyz87@gmail.com> | 2026-10-26 16:38:23 -0700 |
|---|---|---|
| committer | Alexei Starovoitov <ast@kernel.org> | 2026-10-27 16:26:21 +0000 |
| commit | 5dd32c8c108226912054e293ee306c98c283c512 (patch) | |
| tree | f32682f5fc322cf8e4985069373e04ae8c3e849e /kernel | |
| parent | 0c33d612bdd50aaeef263410739060827377d9c0 (diff) | |
bpf: Avoid false infinite loop reports after changes in SCEV clamping
Clamping now reproduces the widened range at every back edge,
so induction variable ranges no longer vary between iterations.
This can produce identical checkpoint states in terminating programs,
for example:
int main(...) {
for (int i = 0; i < 10; i++) {
foo(...);
for (int j = 0; j < 3; j++)
...;
}
}
void foo(...) {
...;
}
Handle two cases in states.c:bpf_is_state_visited():
- For checkpoints inside foo(), look for active loops in upper frames
as well. Otherwise current_loop() returns NULL and an identical
callee state can trigger an infinite loop report.
- For checkpoints inside the inner loop, skip the infinite loop check
when the current state is inside a terminating loop in any frame.
That loop bounds how many times its body is re-executed, so an
identical state on a later iteration does not imply nontermination.
This covers the inner loop above, re-entered with a fresh entry
state on each outer iteration, and inner loops SCEV cannot analyze.
The nested_terminating_prune test still rejects such an outer loop,
but now reaches the jump complexity limit instead of reporting an
infinite loop. Update its expected message accordingly.
Fixes: 6ccf49f79327 ("bpf: Use SCEV to widen bounded loops")
Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Link: https://patch.msgid.link/20261026-scev-simpler-clamp-v2-3-f373aec9825f@gmail.com
Diffstat (limited to 'kernel')
| -rw-r--r-- | kernel/bpf/states.c | 36 |
1 files changed, 28 insertions, 8 deletions
diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c index d7df6632a5c4..a6ae86f973e3 100644 --- a/kernel/bpf/states.c +++ b/kernel/bpf/states.c @@ -1358,13 +1358,33 @@ static bool need_loop_checkpoint(struct bpf_verifier_env *env, int insn_idx) return top->loop_id == insn_idx && top->terminates; } +/* Searches for an innermost loop entry starting from an innermost frame. */ static struct loop_stack_entry *current_loop(struct bpf_verifier_state *st) { - struct bpf_func_state *frame = st->frame[st->curframe]; + struct bpf_func_state *frame; + int fr; - if (frame->loop_stack_cnt == 0) - return NULL; - return &frame->loop_stack[frame->loop_stack_cnt - 1]; + for (fr = st->curframe; fr >= 0; fr--) { + frame = st->frame[fr]; + if (frame->loop_stack_cnt) + return &frame->loop_stack[frame->loop_stack_cnt - 1]; + } + return NULL; +} + +/* True if any of the loops call chain is currently in terminates. */ +static bool in_a_terminating_loop(struct bpf_verifier_state *st) +{ + struct bpf_func_state *frame; + int l, fr; + + for (fr = st->curframe; fr >= 0; fr--) { + frame = st->frame[fr]; + for (l = frame->loop_stack_cnt - 1; l >= 0; l--) + if (frame->loop_stack[l].terminates) + return true; + } + return false; } int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx) @@ -1528,10 +1548,10 @@ int bpf_is_state_visited(struct bpf_verifier_env *env, int insn_idx) */ old_loop = current_loop(&sl->state); cur_loop = current_loop(cur); - if (old_loop && cur_loop && - old_loop->terminates && cur_loop->terminates && - old_loop->entry_state == cur_loop->entry_state) { - if (states_equal(env, &sl->state, cur, RANGE_WITHIN)) { + if (old_loop && cur_loop && in_a_terminating_loop(cur)) { + if (old_loop->terminates && cur_loop->terminates && + old_loop->entry_state == cur_loop->entry_state && + states_equal(env, &sl->state, cur, RANGE_WITHIN)) { loop = true; goto hit; } |
