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 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.