BPF List
 help / color / mirror / Atom feed
From: Eduard Zingerman <eddyz87@gmail.com>
To: Alexei Starovoitov <alexei.starovoitov@gmail.com>
Cc: Andrii Nakryiko <andrii.nakryiko@gmail.com>,
	Andrew Werner <awerner32@gmail.com>, bpf <bpf@vger.kernel.org>,
	Andrei Matei <andreimatei1@gmail.com>,
	Tamir Duberstein <tamird@gmail.com>,
	Joanne Koong <joannelkoong@gmail.com>,
	kernel-team@dataexmachina.dev, Song Liu <song@kernel.org>,
	Kumar Kartikeya Dwivedi <memxor@gmail.com>
Subject: Re: [BUG] verifier escape with iteration helpers (bpf_loop, ...)
Date: Wed, 04 Oct 2023 04:05:30 +0300	[thread overview]
Message-ID: <3d88ede5cbe38ae96be0c148770454b2344fdcce.camel@gmail.com> (raw)
In-Reply-To: <20231003230820.iazvofhysfmurwon@MacBook-Pro-49.local>

On Tue, 2023-10-03 at 16:08 -0700, Alexei Starovoitov wrote:
[...]
> > When bpf_iter_next() is reached in state C and states_equal() shows
> > that there is potentially equivalent state V:
> > - copy C' of C is created sing copy_verifier_state(), it updates all
> >   branch counters up the ownership chain as with any other state;
> > - C' is put to env->loop_stack.
> 
> and C' points to normal parent and increments its branches as part of __push_stack().

Yes.

> void foo()
> {
>   if (...) { .. } // no pruning
>   if (...) { .. } // no pruning
> 
>   bpf_for(...)
>   if (...) { .. } // all ok
> }
> 
> Essentially any complex code before the loop has a high chance of the verifier
> state explosion.

Yes. This may be an issue. I'll try to hack a layered variant I talked
before to see what are the underlying issues. The idea is to verify
every child state of the loop entry before moving to any branches of
loop's parent states. This would allow to compute local "steady loop"
and mark everything after loop entry as safe (.branches == 0).

> > It is possible and correct to propagate liveness and precision from V
> > to C when loop steady state is achieved, as at that point we know for
> > sure that C is a sub-state of V. However, currently loop states are
> > tracked globally and no states are processed after loops steady state
> > is reached, hence I don't do liveness and precision propagation.
> 
> Hmm. I think the code is doing it:
> if (is_iter_next_insn(env, insn_idx)) {
>   if (states_equal(env, &sl->state, cur)) {
>     push_loop_stack(env, insn_idx, env->prev_insn_idx);
>     goto hit;
> hit:
>   propagate_liveness()

Yes and this should be changed. It should not produce incorrect
results as additional precision and read marks are conservative,
but will hinder state pruning in some scenarios.

> DFS + BFS traversal doesn't guarantee safety.
> We delayed looping states in loop_stack, but it doesn't mean
> that the loop body reached a steady state.
> Your own num_iter_bug() is an example.
> The verifier will miss exploring path with r7 = 0xdead.
> When C' is popped from loop_stack there is chance it will explore them,
> but there is no guarantee.
> For this particular num_iter_bug() case the loop processing
> will eventually propagate precision marks and retrying with C' later,
> the C' will be correctly rejected, since precision marks are there.
> But I think that is more due to luck and the shape of the test.
> 
> if (is_iter_next_insn(env, insn_idx)) {
>   if (states_equal(env, &sl->state, cur)) {
> 
> is a foot gun. It finds broken equivalence and potentially skipping
> whole blocks of code.
> What guarantees that C' from loop_stack will explore them?

The idea is that when `env->stack.size == 0` all V's non-delayed
children states are explored and relevant precision marks are
propagated to V. If at this point some states_equal(V, C') is false it
is necessary to schedule C' for one more exploration round as it might
visit some code blocks that were not visited on the path from V to C'
(as different predictions decisions could be made).
If a point is reached when for all loop states C' there are some
states V such that states_equal(V, C'), there are no more
configurations that can visit code blocks not visited on the path from
V to C', as prediction decisions would be the same.

> I think avoiding states_equal(V, cur) when V state didn't converge
> is a mandatory part of the fix.

But there are no other heuristics that suggest that exploration of the
infinite series of loop iterations could be stopped for current path.

> I can see that little bit of out-of-order state processing probably
> is still correct, but delaying loop_stack all the way until 
> env->stack.size == 0 is just broken.
> For example we can push looping state in process_iter_next_call()
> not the top of the stack, but place it after branches with
> insn_idx >= iter_next's insn_idx.

This should be based on state parentage chain as code blocks could be
non-linearly structured.

> This way the delayed looping states will still be processed
> around loop processing and every code block before the loop
> will converge to branches = 0.
> To do that we don't need another loop_stack.

Will comment in the morning.

  parent reply	other threads:[~2023-10-04  1:05 UTC|newest]

Thread overview: 52+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-07-07 14:04 [BUG] verifier escape with iteration helpers (bpf_loop, ...) Andrew Werner
2023-07-07 16:44 ` Alexei Starovoitov
2023-07-07 18:08   ` Andrii Nakryiko
2023-07-07 18:21     ` Andrew Werner
2023-09-17 21:37     ` Eduard Zingerman
2023-09-17 22:09       ` Kumar Kartikeya Dwivedi
2023-09-18 13:06         ` Eduard Zingerman
2023-09-19 16:28           ` Eduard Zingerman
2023-09-19 23:02             ` Andrii Nakryiko
2023-09-20  0:19               ` Eduard Zingerman
2023-09-20 16:20                 ` Eduard Zingerman
2023-09-20 16:57                   ` Andrii Nakryiko
2023-09-21  9:14                 ` Alexei Starovoitov
2023-09-21 11:03                   ` Eduard Zingerman
2023-09-21 12:56                     ` Alexei Starovoitov
2023-09-21 16:23                       ` Eduard Zingerman
2023-09-21 16:35                         ` Andrii Nakryiko
2023-09-21 18:16                           ` Eduard Zingerman
2023-09-22  1:01                             ` Eduard Zingerman
2023-09-22  2:48                               ` Andrii Nakryiko
2023-09-22 18:36                                 ` Eduard Zingerman
2023-09-22 20:52                                   ` Andrii Nakryiko
2023-09-25  1:01                                     ` Eduard Zingerman
2023-09-26  0:33                                       ` Andrii Nakryiko
2023-09-26 15:55                                         ` Eduard Zingerman
2023-09-26 16:25                                           ` Andrii Nakryiko
2023-09-28  1:09                                             ` Eduard Zingerman
2023-09-28 18:30                                               ` Andrii Nakryiko
2023-10-02  3:26                                                 ` Eduard Zingerman
2023-09-30  0:41                                               ` Alexei Starovoitov
2023-10-02  1:40                                                 ` Eduard Zingerman
2023-10-02 16:29                                                   ` Alexei Starovoitov
2023-10-02 17:18                                                     ` Eduard Zingerman
2023-10-03  0:05                                                       ` Alexei Starovoitov
2023-10-03  2:00                                                         ` Alexei Starovoitov
2023-10-03 15:33                                                         ` Eduard Zingerman
2023-10-03 16:07                                                           ` Alexei Starovoitov
2023-10-03 18:50                                                           ` Alexei Starovoitov
2023-10-03 21:52                                                             ` Eduard Zingerman
2023-10-03 22:03                                                               ` Eduard Zingerman
2023-10-03 23:08                                                               ` Alexei Starovoitov
2023-10-03 23:14                                                                 ` Eduard Zingerman
2023-10-04  0:22                                                                   ` Andrii Nakryiko
2023-10-04  1:05                                                                 ` Eduard Zingerman [this message]
2023-10-04  2:57                                                                   ` Alexei Starovoitov
2023-10-04  5:50                                                                     ` Alexei Starovoitov
2023-10-04  9:49                                                                       ` Eduard Zingerman
2023-10-04 11:52                                                                     ` Eduard Zingerman
2023-09-19 23:14       ` Andrii Nakryiko
2023-09-20  0:06         ` Eduard Zingerman
2023-09-20 16:37           ` Andrii Nakryiko
2023-09-20 17:13             ` 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=3d88ede5cbe38ae96be0c148770454b2344fdcce.camel@gmail.com \
    --to=eddyz87@gmail.com \
    --cc=alexei.starovoitov@gmail.com \
    --cc=andreimatei1@gmail.com \
    --cc=andrii.nakryiko@gmail.com \
    --cc=awerner32@gmail.com \
    --cc=bpf@vger.kernel.org \
    --cc=joannelkoong@gmail.com \
    --cc=kernel-team@dataexmachina.dev \
    --cc=memxor@gmail.com \
    --cc=song@kernel.org \
    --cc=tamird@gmail.com \
    /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