From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v3 18/45] bpf: Tell the passes over the graph where an added may_goto goes
Date: Fri, 06 Nov 2026 04:26:33 +0000 [thread overview]
Message-ID: <sashiko-outbox-200719@kernel.org> (raw)
In-Reply-To: <20261106035111.3315285-19-alexei.starovoitov@gmail.com>
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] bpf: missing head guard target edge in update_insn() stack liveness
- [High] bpf: bpf_prune_dead_branches() folds jumps before head guards are placed
--
commit dc67a790eecafbb6bb6ef2d65c1ca1f5bfcd6434
Author: Alexei Starovoitov <ast@kernel.org>
bpf: Tell the passes over the graph where an added may_goto goes
This commit places loop guards right after loop discovery so pre-passes over
the control flow graph account for may_goto edges added at loop headers, and
extends bpf_edge_leaves_loop() to propagate constants when identifying loop
latches controlled by flags.
> diff --git a/kernel/bpf/liveness.c b/kernel/bpf/liveness.c
> index 525c0faff1a66..6a2ee407412b8 100644
> --- a/kernel/bpf/liveness.c
> +++ b/kernel/bpf/liveness.c
[ ... ]
> @@ -2780,6 +2796,7 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
> u32 new_out = 0;
> u32 new_in = 0;
> u32 use;
> + int t;
>
> succ = bpf_insn_successors(env, insn_idx);
> for (int s = 0; s < succ->cnt; ++s)
> @@ -2794,6 +2811,10 @@ int bpf_compute_live_registers(struct bpf_verifier_env *env)
> use |= state[insn_idx + insns[insn_idx].imm + 1].in &
> mask_widen(GENMASK(BPF_REG_5, BPF_REG_1));
> new_in = (new_out & ~live->def) | use;
> + /* may_goto that is added at the head of a loop goes there */
> + t = bpf_head_guard_target(env, insn_idx);
> + if (t >= 0)
> + new_in |= state[t].in;
> if (new_out != live->out || new_in != live->in) {
> live->in = new_in;
> live->out = new_out;
[Severity: High]
As reported by Sashiko on v2 [1], bpf_compute_live_registers() unions
state[t].in into new_in for t = bpf_head_guard_target(env, insn_idx), and
compute_subprog_args() joins the forward pointer and spill state from the
loop header into t, but does update_insn() in kernel/bpf/liveness.c also need
to union FM_LIVE_BEFORE of t into new_before for stack slot liveness?
In bpf_compute_subprog_arg_access() -> analyze_subprog() ->
update_instance() -> update_insn(), backward stack liveness only inspects
normal CFG successors:
kernel/bpf/liveness.c:update_insn() {
...
for (s = 0; s < succ->cnt; ++s) {
unsigned long *succ_live;
succ_live = rel_mask(fm, relative_idx(instance, succ->items[s]), FM_LIVE_BEFORE);
for (w = 0; w < fm->words; w++)
new_after[w] |= succ_live[w];
}
...
for (w = 0; w < fm->words; w++) {
unsigned long new_before = (new_after[w] & ~must_write[w]) | may_read[w];
...
}
If a stack slot in the current or caller frame is initialized before the
loop, overwritten (FM_MUST_WRITE) on every normal CFG path through the loop
to the exit t (or by the loop header instruction insn_idx itself), and then
read at or after t, update_insn() clears its liveness bit via ~must_write[w]
and leaves FM_LIVE_BEFORE as 0 at the loop header insn_idx.
When a verifier state reaches insn_idx during the main pass,
bpf_clean_verifier_state() -> clean_verifier_state() -> __clean_func_state()
sees bpf_stack_slot_alive() as false, resets spilled_ptr to NOT_INIT, and
marks the slot as STACK_POISON before the state is sent along the header
may_goto edge to t. Can that cause valid stack reads at or after t to be
rejected, or cause __clean_func_state() and join_slot() at t to mishandle
spilled pointers or uninitialized stack slots that were prematurely poisoned
at the loop header?
> diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
> index 8c89bf67a8424..5af42d2dcab94 100644
> --- a/kernel/bpf/verifier.c
> +++ b/kernel/bpf/verifier.c
> @@ -25219,6 +25219,21 @@ int bpf_check(struct bpf_prog **prog, union bpf_attr *attr, bpfptr_t uattr,
> if (ret < 0)
> goto skip_full_check;
>
> + /*
> + * It took the constants to remove dead branches and find the loops. Now it's known
> + * which edges the verifier may add, see bpf_head_guard_target(). What follows
> + * believes the constants.
> + */
> + ret = bpf_flow_place_guards(env);
> + if (ret > 0)
> + ret = bpf_compute_const_regs(env);
> + if (ret < 0)
> + goto skip_full_check;
[Severity: High]
When bpf_flow_place_guards() returns a positive value and bpf_check()
re-runs bpf_compute_const_regs(), what happens to conditional jumps at or
after a head-guard target that were already rewritten in-place by
bpf_prune_dead_branches() earlier in bpf_check()?
Earlier in bpf_check(), the first bpf_compute_const_regs() pass and
bpf_prune_dead_branches() run before bpf_compute_loops() and
bpf_flow_place_guards():
kernel/bpf/verifier.c:bpf_check() {
...
ret = bpf_compute_const_regs(env);
if (ret < 0)
goto skip_full_check;
ret = bpf_prune_dead_branches(env);
if (ret < 0)
goto skip_full_check;
...
}
At that point aux[h].guard_at_header is not yet set. If a register (or a
read-only map load derived from it, such as r7 == &ro on normal exits versus
r7 == &rw at the loop header) resolves to a constant at the exit target t in
the first bpf_compute_const_regs() pass, bpf_prune_dead_branches()
permanently rewrites any conditional jump testing that constant at or after t
into BPF_JMP_A(taken ? insn->off : 0) and recomputes the CFG postorder.
Re-running bpf_compute_const_regs() after bpf_flow_place_guards() clears
aux->const_reg_mask at t, but won't the in-place BPF_JMP_A instruction
rewrites in env->prog->insnsi remain in effect when a state jumps from the
loop header to t via may_goto?
[1] https://lore.kernel.org/r/20261105204513.2230344-19-alexei.starovoitov@gmail.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106035111.3315285-1-alexei.starovoitov@gmail.com?part=18
next prev parent reply other threads:[~2026-11-06 4:26 UTC|newest]
Thread overview: 77+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-06 3:50 [PATCH bpf-next v3 00/45] bpf: A verifier that joins states Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 01/45] bpf: Add may_goto to the loops that the verifier marks Alexei Starovoitov
2026-11-06 4:22 ` sashiko-bot
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 02/45] bpf: Limit depth and time of recursive subprogs at run time Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 03/45] bpf: Add union and widening of the range of a register Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 04/45] bpf: Add join of registers and stack slots Alexei Starovoitov
2026-11-06 4:39 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 05/45] bpf: Add join of verifier states Alexei Starovoitov
2026-11-06 4:25 ` sashiko-bot
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 06/45] bpf: Tell which arguments of a call a join must leave alone Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 07/45] bpf: Add the data structures of the main pass over the control flow graph Alexei Starovoitov
2026-11-06 4:24 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 08/45] bpf: Order the insns, find what stops a loop and where may_goto can go Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 09/45] bpf: Find the registers that hold a number or a pointer to arena Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 10/45] bpf: Find the registers and stack slots that later checks depend on Alexei Starovoitov
2026-11-06 4:08 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 11/45] bpf: Add the sets of states at an insn and the entries of functions Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 12/45] bpf: Compare states of different iterations of a loop Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 13/45] bpf: Decide which states are not joined Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 14/45] bpf: Merge the state that arrives into the set at an insn Alexei Starovoitov
2026-11-06 4:24 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 15/45] bpf: Move states along edges, into loops and subprogs and out of them Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 16/45] bpf: Make the state after a call from the caller's state and an exit state Alexei Starovoitov
2026-11-06 4:34 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 17/45] bpf: Enter and leave loops in the main pass, build flow.c Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 18/45] bpf: Tell the passes over the graph where an added may_goto goes Alexei Starovoitov
2026-11-06 4:26 ` sashiko-bot [this message]
2026-11-06 3:50 ` [PATCH bpf-next v3 19/45] bpf: Verify programs as a fixpoint over the control flow graph Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 4:55 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 20/45] bpf: Keep the count of a loop when a register has no closed form Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 21/45] bpf: Verify static subprogs on their own Alexei Starovoitov
2026-11-06 4:50 ` bot+bpf-ci
2026-11-06 4:53 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 22/45] bpf: Allow recursive subprogs Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 5:09 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 23/45] bpf: Remove is_state_visited(), bpf_compute_scc() and iter.depth Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 24/45] bpf: Remove precision tracking Alexei Starovoitov
2026-11-06 4:24 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 25/45] bpf: Remove prune points, levels of state comparison and pop_stack() Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 26/45] bpf: Update comments about paths, pruning and explored states Alexei Starovoitov
2026-11-06 4:19 ` sashiko-bot
2026-11-06 4:24 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 27/45] bpf: Track a pointer to one of a set of functions Alexei Starovoitov
2026-11-06 4:49 ` sashiko-bot
2026-11-06 3:50 ` [PATCH bpf-next v3 28/45] bpf: callx reads what the subprogs whose address is taken read Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 29/45] bpf: Don't take what bpf_core_cast() returns for a number Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 30/45] bpf: Don't widen away a register that only the loop sets Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 31/45] bpf: A base that nothing is known about can be the context Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 32/45] bpf: Keep the frames of the caller for a subprog that loads data_end Alexei Starovoitov
2026-11-06 4:24 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 33/45] bpf: Keep the frames of the caller for ctx of struct_ops with a reference Alexei Starovoitov
2026-11-06 4:24 ` bot+bpf-ci
2026-11-06 3:50 ` [PATCH bpf-next v3 34/45] bpf: R0 is live at the exit that an added may_goto goes to Alexei Starovoitov
2026-11-06 3:50 ` [PATCH bpf-next v3 35/45] bpf: Keep the step of a number in the union of two ranges Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 36/45] bpf: Keep the first iteration of a counted loop apart Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:51 ` [PATCH bpf-next v3 37/45] bpf: What a call returns in each iteration is like what the loop loads Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 38/45] bpf: Find where the jumps of a loop stop a number Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 39/45] selftests/bpf: Adjust tests to log lines that are gone Alexei Starovoitov
2026-11-06 4:38 ` bot+bpf-ci
2026-11-06 3:51 ` [PATCH bpf-next v3 40/45] selftests/bpf: Adjust tests to new verdicts, messages and order Alexei Starovoitov
2026-11-06 4:25 ` bot+bpf-ci
2026-11-06 4:34 ` sashiko-bot
2026-11-06 3:51 ` [PATCH bpf-next v3 41/45] selftests/bpf: Test states that are not joined Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 42/45] selftests/bpf: Test static subprogs that are verified on their own Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 43/45] selftests/bpf: Test loops that are counted, walked or widened Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 44/45] selftests/bpf: Test progs that run out of budget Alexei Starovoitov
2026-11-06 3:51 ` [PATCH bpf-next v3 45/45] selftests/bpf: Test callx of one of a set of functions Alexei Starovoitov
2026-11-06 4:52 ` sashiko-bot
2026-11-06 18:08 ` [PATCH bpf-next v3 00/45] bpf: A verifier that joins states 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-200719@kernel.org \
--to=sashiko-bot@kernel.org \
--cc=alexei.starovoitov@gmail.com \
--cc=bpf@vger.kernel.org \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox