From: sashiko-bot@kernel.org
To: "Alexei Starovoitov" <alexei.starovoitov@gmail.com>, bpf@vger.kernel.org
Subject: Re: [PATCH bpf-next v3 05/45] bpf: Add join of verifier states
Date: Fri, 06 Nov 2026 04:25:56 +0000 [thread overview]
Message-ID: <sashiko-outbox-200717@kernel.org> (raw)
In-Reply-To: <20261106035111.3315285-6-alexei.starovoitov@gmail.com>
Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Medium] bpf: asymmetric unallocated and poisoned stack handling in join_func()
- [Medium] bpf: join_reg() zeroing dst->delta breaks explore_alu_limits in join_check()
- [Medium] bpf: asymmetric out_stack_arg_cnt check in join_func() blocks valid joins
--
commit 1d1d176d0db968688ddcdf9cb26ad2c24d9339f4
Author: Alexei Starovoitov <ast@kernel.org>
bpf: Add join of verifier states
This commit adds the second half of kernel/bpf/join.c and enables building it.
It implements bpf_states_join() to merge two verifier states frame by frame at
a single instruction and verifies that the joined state subsumes both inputs.
> diff --git a/kernel/bpf/join.c b/kernel/bpf/join.c
> index fe513b7a8e884..4da051767e1d9 100644
> --- a/kernel/bpf/join.c
> +++ b/kernel/bpf/join.c
> @@ -398,3 +398,250 @@ static bool join_slot(struct bpf_join_state *js, struct bpf_stack_state *ds,
> bpf_mark_reg_not_init(js->env, &ds->spilled_ptr);
> return true;
> }
> +
[ ... ]
> +static bool join_func(struct bpf_join_state *js, struct bpf_func_state *fd,
> + const struct bpf_func_state *fa, const struct bpf_func_state *fb,
> + u16 live_regs, int pass)
> +{
[ ... ]
> + nslots_b = bpf_stack_nr_slots(fb);
> + for (i = 0; i < bpf_stack_nr_slots(fa); i++) {
> + const struct bpf_stack_state *sb = i < nslots_b ? bpf_stack_slot(fb, i) : NULL;
> +
> + js->place = MAX_BPF_REG + i;
> + if (!join_slot(js, bpf_stack_slot(fd, i), bpf_stack_slot(fa, i), sb, pass))
> + return false;
> + }
> + js->place = -1;
> + /* Slots that exist only in 'b' are STACK_INVALID in the result. */
> + for (i = bpf_stack_nr_slots(fa); pass == 1 && i < nslots_b; i++) {
> + const struct bpf_stack_state *sb = bpf_stack_slot(fb, i);
> +
> + if (slot_is_special(sb) ||
> + (slot_has_spill(sb) && sb->spilled_ptr.type != SCALAR_VALUE))
> + return false;
> + }
[Severity: Medium]
As reported by Sashiko on v2 [1], how does bpf_states_join() -> join_func() ->
join_slot() handle stack slots when !env->allow_uninit_stack?
First, in the loop over slots that exist only in fb (for i >=
bpf_stack_nr_slots(fa)), join_func() only rejects special slots and non-scalar
spills, without checking !js->env->allow_uninit_stack. If fb has a live
initialized slot at i >= bpf_stack_nr_slots(fa) (such as a scalar STACK_SPILL,
STACK_MISC, or STACK_ZERO) while fa has allocated_stack <= i * 8, the join
succeeds and leaves slot i unallocated in fd. If the program later reads slot
i under a condition that only holds along path b, won't
check_stack_slot_within_bounds() falsely reject a valid program with -EACCES
when !env->allow_uninit_stack?
Second, when bpf_clean_verifier_state() marks dead stack slots in fa as
STACK_POISON via __clean_func_state(), and slot i is unallocated in fb (so
join_slot() substitutes invalid_slot with STACK_INVALID), join_slot() runs:
if (!js->env->allow_uninit_stack)
return !memcmp(sa->slot_type, sb->slot_type, sizeof(sa->slot_type));
Won't memcmp() treat dead STACK_POISON bytes in sa and unallocated
STACK_INVALID bytes in sb as unequal, causing dead stack slots to prevent a
join when !env->allow_uninit_stack?
> +
> + if (fa->out_stack_arg_cnt < fb->out_stack_arg_cnt)
> + return false;
[Severity: Medium]
As reported by Sashiko on v2 [2], should join_func() reject the join whenever
fa->out_stack_arg_cnt < fb->out_stack_arg_cnt?
Since out_stack_arg_cnt is an allocation high-water mark grown by
grow_stack_arg_slots() in check_stack_arg_write(), it is not reset to 0 when
invalidate_outgoing_stack_args() or bpf_flow_return() marks
stack_arg_regs[0..out_stack_arg_cnt - 1] as NOT_INIT after a call returns.
If state a reaches a loop header or branch target before any call with stack
arguments (fa->out_stack_arg_cnt == 0) and state b arrives after a call with
stack arguments whose slots are now NOT_INIT (fb->out_stack_arg_cnt > 0),
won't this check prevent joining or widening the states even though slots at
i >= fa->out_stack_arg_cnt are implicitly NOT_INIT in fa and fd?
[ ... ]
> +/* The result has to describe everything that an input describes */
> +static int join_check(struct bpf_join_state *js, struct bpf_verifier_state *dst,
> + const struct bpf_verifier_state *in, const char *which)
> +{
> + struct bpf_verifier_env *env = js->env;
> +
> + if (bpf_state_subsumes(env, dst, (struct bpf_verifier_state *)in))
> + return 0;
> + /* check_ids() has no room left: that says nothing about the join */
> + if (env->idmap_scratch.cnt == BPF_ID_MAP_SIZE)
> + return 1;
> + bpf_log(&env->log, "join:");
> + print_verifier_state(env, dst, dst->curframe, true);
> + bpf_log(&env->log, "input %s:", which);
> + print_verifier_state(env, in, in->curframe, true);
> + verifier_bug(env, "join of states at insn %u does not subsume input '%s'",
> + dst->insn_idx, which);
> + return -EFAULT;
> +}
[Severity: Medium]
As reported by Sashiko on v2 [3], can bpf_states_join() trigger verifier_bug()
in join_check() when env->explore_alu_limits is true?
In join_reg() for SCALAR_VALUE:
case SCALAR_VALUE:
if (js->env->explore_alu_limits && !reg_value_equal(ra, rb))
return false;
if (pass == 1) {
jid_note(js, ra->id, rb->id, scalar_link_consistent(ra, rb));
return true;
}
join_range(js, dst, ra, rb);
/* a link that exists on one side only is dropped */
if (ra->id && !jid_good(js, ra->id, rb->id)) {
dst->id = 0;
dst->delta = 0;
}
return true;
Two SCALAR_VALUE registers ra and rb with equal ranges and the same non-zero
delta pass reg_value_equal(ra, rb). If their IDs do not form a valid bijection
across the state (so jid_good(js, ra->id, rb->id) is false in pass 2),
join_reg() clears dst->id = 0 and dst->delta = 0.
When join_check() subsequently calls bpf_state_subsumes() -> states_equal() ->
func_states_equal() -> regsafe(), regsafe() checks:
if (env->explore_alu_limits) {
return memcmp(rold, rcur, offsetof(struct bpf_reg_state, id)) == 0 &&
check_scalar_ids(rold->id, rcur->id, idmap);
}
Because delta lies before id in struct bpf_reg_state, won't memcmp() fail when
comparing dst->delta (0) against ra->delta (non-zero), causing join_check() to
hit verifier_bug() and return -EFAULT?
[1] https://lore.kernel.org/r/20261105204513.2230344-6-alexei.starovoitov@gmail.com
[2] https://lore.kernel.org/r/20261105204513.2230344-6-alexei.starovoitov@gmail.com
[3] https://lore.kernel.org/r/20261105204513.2230344-6-alexei.starovoitov@gmail.com
--
Sashiko AI review · https://sashiko.dev/#/patchset/20261106035111.3315285-1-alexei.starovoitov@gmail.com?part=5
next prev parent reply other threads:[~2026-11-06 4:25 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 [this message]
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
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-200717@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.