From: Eduard Zingerman <eddyz87@gmail.com>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: bpf <bpf@vger.kernel.org>, Alexei Starovoitov <ast@kernel.org>,
Andrii Nakryiko <andrii@kernel.org>,
Daniel Borkmann <daniel@iogearbox.net>,
Martin KaFai Lau <martin.lau@linux.dev>,
Kernel Team <kernel-team@fb.com>,
Yonghong Song <yonghong.song@linux.dev>
Subject: Re: [PATCH bpf-next v1 1/2] bpf: states with loop entry have incomplete read/precision marks
Date: Fri, 21 Mar 2025 13:02:13 -0700 [thread overview]
Message-ID: <a4f1e259a083698bf511b9194be9bff5ba46eb05.camel@gmail.com> (raw)
In-Reply-To: <CAADnVQJabnCzo6=3uATv3nN2hz7Av=BAORVS=hgnyNHt+5dBCw@mail.gmail.com>
On Mon, 2025-03-17 at 14:46 -0700, Alexei Starovoitov wrote:
[...]
> > Looks like there are only two options left:
> > a. propagate read/precision marks in state graph until fixed point is
> > reached;
> > b. switch to CFG based DFA analysis (will need slot/register type
> > propagation to identify which scalars can be precise).
> >
> > (a) is more in line with what we do currently and probably less code.
> > But I expect that it would be harder to think about in case if
> > something goes wrong (e.g. we don't print/draw states graph at the
> > moment, I have a debug patch for this that I just cherry-pick).
>
> if (a) is not a ton of code it's worth giving it a shot,
> but while(!converged) propagate_precision()
> will look scary.
> We definitely need to do (b) at the end.
I tried a simpler approach we talked about on Wednesday.
Here is an implementation using strongly connected components:
https://github.com/eddyz87/bpf/tree/scc-instead-of-loop-entry
The idea is as follows:
- Compute strongly connected components on control flow graph;
- The number of such components is not big for selftests and
sched_ext;
- For each component track the following:
- A flag indicating if states loop had ever been formed by states
originating in this component
(states loop is states_equal(...RANGE_WITHIN)).
When such flag is set the states within the component need to be
compared using RANGE_WITHIN logic.
- A counter for "yet to be explored / currently explored" states
originating from the component + an epoch counter. The epoch
counter is incremented each time the "yet to be explored" counter
reaches zero. Verifier states are extended to keep an epoch
counter if states insn_idx is within the component. The epoch
counter is needed to know if two verifier states are within the
same states sub-tree.
- in clean_live_states() mark all state registers/stack slots as read
and precise if:
- state->insn_idx is within a strongly connected component;
- state loops had been formed for states within this component;
- state's component's epoch differs from current component's epoch,
meaning that sub-tree of child states had already been explored
and there would be no need to apply widening within this component.
This almost works.
It comes short for a single selftest:
int cond_break1(const void *ctx)
{
unsigned long i;
unsigned int sum = 0;
for (i = zero; i < ARR_SZ && can_loop; i++) // Loop A
sum += i;
for (i = zero; i < ARR_SZ; i++) { // Loop B
barrier_var(i);
sum += i + arr[i];
cond_break;
}
return sum;
}
The test is still verified, but the number of explored states goes
from 10 to 8K. The problem here is that the loop B converges first and
`clean_live_states()` marks `sum` as precise at entry to loop B.
Then, when a branch originating from Loop A reaches entry to loop B it
hits a checkpoint and propagates `sum` to the `may_goto` checkpoint at
the beginning of the Loop A.
Hence, widening logic for `sum` does not kick in.
There is also a problem with one sched_ext program (lavd_dispatch),
where eager precision marks applied to cached state confuse
mark_chain_precision, at it hits a bug assert when trying to mark r1
precise when backtracking a function call. This is something to debug
further.
---
I also tried a more complex approach, where:
- state loop backedges are accumulated for each strongly connected
component;
- when component is done exploring (using same branch/epoch logic as
above) do propagate_{liveness,precision} along the backedges while
precision marks change.
It is here:
https://github.com/eddyz87/bpf/tree/read-marks-stead-state-per-scc
The test cases at hand pass (absent_mark_in_the_middle_state{,2}),
but it hits memory protection errors on complete test_progs run and
requires further debugging.
Overall, I don't like the level of complexity this approach involves.
It appears that it would be better to invest this complexity into a
compiler style pass computing a conservative approximation of precise
registers / stack slots.
prev parent reply other threads:[~2025-03-21 20:02 UTC|newest]
Thread overview: 9+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-03-12 3:13 [PATCH bpf-next v1 1/2] bpf: states with loop entry have incomplete read/precision marks Eduard Zingerman
2025-03-12 3:13 ` [PATCH bpf-next v1 2/2] selftests/bpf: test case with missing read/precision mark Eduard Zingerman
2025-03-13 19:28 ` [PATCH bpf-next v1 1/2] bpf: states with loop entry have incomplete read/precision marks Eduard Zingerman
2025-03-14 17:41 ` Eduard Zingerman
2025-03-15 2:51 ` Alexei Starovoitov
2025-03-15 6:04 ` Eduard Zingerman
2025-03-17 20:28 ` Eduard Zingerman
2025-03-17 21:46 ` Alexei Starovoitov
2025-03-21 20:02 ` Eduard Zingerman [this message]
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=a4f1e259a083698bf511b9194be9bff5ba46eb05.camel@gmail.com \
--to=eddyz87@gmail.com \
--cc=alexei.starovoitov@gmail.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=kernel-team@fb.com \
--cc=martin.lau@linux.dev \
--cc=yonghong.song@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.