From: sashiko-bot@kernel.org
To: "Eduard Zingerman" <eddyz87@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next 03/11] bpf: look for current loop in upper frames in states_equal()
Date: Sat, 24 Oct 2026 00:27:18 +0000 [thread overview]
Message-ID: <sashiko-outbox-183459@kernel.org> (raw)
In-Reply-To: <20261023-scev-simpler-clamp-v1-3-d43a07eda7af@gmail.com>
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Medium] bpf: current_loop() ignores outer loops when inner loop is re-entered
--
commit 999e70b9b80c780cef9257c5cc41cf3820aa644f
Author: Eduard Zingerman <eddyz87@gmail.com>
bpf: look for current loop in upper frames in states_equal()
This commit updates current_loop() to search upper call frames for an active
loop entry when checking if the current loop exploration terminates in
bpf_is_state_visited(). This allows checkpoints inside callees invoked from a
bounded loop to be pruned and to skip false-positive infinite loop checks.
> diff --git a/kernel/bpf/states.c b/kernel/bpf/states.c
> index d7df6632a5c47..306ad208a8e41 100644
> --- a/kernel/bpf/states.c
> +++ b/kernel/bpf/states.c
> @@ -1361,10 +1361,14 @@ static bool need_loop_checkpoint(struct bpf_verifier_env *env, int insn_idx)
> static struct loop_stack_entry *current_loop(struct bpf_verifier_state *st)
> {
> struct bpf_func_state *frame = st->frame[st->curframe];
> + 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];
> + }
[Severity: Medium]
What happens if a terminating outer loop enters a terminating inner loop
(either in a callee or nested in the same function) on a subsequent outer
iteration without having been pruned at the outer loop header?
On each fresh entry into the inner loop, handle_loop_entry_exit() allocates a
new entry_state via bpf_split_cur_state():
kernel/bpf/verifier.c:handle_loop_entry_exit() {
...
/* Create a loop_entry checkpoint before widening */
err = bpf_split_cur_state(env);
if (err)
return err;
entry->min_header_count = iters.min_header_count;
entry->max_header_count = iters.max_header_count;
entry->entry_state = cur->parent;
entry->terminates = true;
...
}
When the outer loop takes its backedge into a second iteration (for example,
when a 32-bit stack spill becomes STACK_MISC during iteration 1 so the outer
header does not immediately prune) and re-enters the inner loop, the inner
loop receives a distinct entry_state from the one recorded in the first outer
iteration's checkpoint.
Because current_loop() unconditionally returns the innermost active loop entry,
bpf_is_state_visited() only compares the inner loop's entry_state pointers:
kernel/bpf/states.c:bpf_is_state_visited() {
...
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)) {
loop = true;
goto hit;
}
goto skip_inf_loop_check;
}
...
}
Since old_loop->entry_state != cur_loop->entry_state for the re-entered inner
loop, this check fails without noticing that an enclosing terminating loop in
the same or upper frame still has a matching entry_state.
Execution then falls through to the infinite loop check, where
loop_stack_safe() does not compare entry_state:
kernel/bpf/states.c:loop_stack_safe() {
...
for (i = 0; i < old->loop_stack_cnt; i++) {
if (old_stack[i].loop_id == cur_stack[i].loop_id &&
old_stack[i].max_header_count == cur_stack[i].max_header_count &&
old_stack[i].terminates == cur_stack[i].terminates)
continue;
return false;
}
...
}
Won't states_maybe_looping() and states_equal(env, &sl->state, cur, EXACT)
then match the inner loop checkpoint from the previous outer iteration and
falsely reject the program with an infinite loop error?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261023-scev-simpler-clamp-v1-0-d43a07eda7af@gmail.com?part=3
next prev parent reply other threads:[~2026-10-24 0:27 UTC|newest]
Thread overview: 22+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-24 0:03 [PATCH bpf-next 00/11] bpf: fixes and follow-ups for 32-/64-bit scalar evolution Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 01/11] bpf: reject pointer operands for 32-bit recurrences/latches Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 02/11] bpf: do not intersect with current register range on clamp Eduard Zingerman
2026-10-24 0:41 ` bot+bpf-ci
2026-10-24 6:57 ` Eduard Zingerman
2026-10-25 1:03 ` Alexei Starovoitov
2026-10-25 5:10 ` Alexei Starovoitov
2026-10-26 18:32 ` Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 03/11] bpf: look for current loop in upper frames in states_equal() Eduard Zingerman
2026-10-24 0:27 ` sashiko-bot [this message]
2026-10-24 0:41 ` bot+bpf-ci
2026-10-24 0:03 ` [PATCH bpf-next 04/11] bpf: check the iteration count before the predicted exit value Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 05/11] bpf: (zext32 (zext32 X)) -> (zext32 X) SCEV simplification rule Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 06/11] bpf: print 32-bit SCEV bounds as 32-bit numbers Eduard Zingerman
2026-10-24 0:41 ` bot+bpf-ci
2026-10-24 0:03 ` [PATCH bpf-next 07/11] selftests/bpf: pointer operands for 32-bit recurrences/latches Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 08/11] selftests/bpf: cases with empty range error under old clamping logic Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 09/11] selftests/bpf: case reporting infinite loop w/o current_loop() changes Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 10/11] selftests/bpf: 32-bit copy of the counter compared at the latch Eduard Zingerman
2026-10-24 0:03 ` [PATCH bpf-next 11/11] selftests/bpf: Restore stack pointer subrange pruning coverage Eduard Zingerman
2026-10-24 5:11 ` [PATCH bpf-next 00/11] bpf: fixes and follow-ups for 32-/64-bit scalar evolution Alexei Starovoitov
2026-10-24 5:33 ` Eduard Zingerman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=sashiko-outbox-183459@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=eddyz87@gmail.com \
--cc=sashiko-reviews@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.