Re: [BUG] verifier escape with iteration helpers (bpf_loop, ...)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Sep 27, 2023 at 6:09 PM Eduard Zingerman <eddyz87@xxxxxxxxx> wrote:
>
>  static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
>  {
>      ...
>      while (sl) {
>          ...
>          if (sl->state.branches) {
>              ...
>              if (is_iter_next_insn(env, insn_idx)) {
>                  if (states_equal(env, &sl->state, cur)) {
>                      ...
>                      iter_state = &func(env, iter_reg)->stack[spi].spilled_ptr;
>                      if (iter_state->iter.state == BPF_ITER_STATE_ACTIVE) {
> +                        // Don't want to proceed with 'cur' verification,
> +                        // push it to iters queue to check again if states
> +                        // are still equal after env->head is exahusted.
> +                        if (env->stack_size != 0)
> +                            push_iter_stack(env, cur, ...);
>                          goto hit;

I suspect that neither option A "Exit or Loop" or B "widening"
are not correct.
In both cases we will do states_equal() with states that have
not reached exit and don't have live/precision marks.

The key aspect of the verifier state pruning is that safe states
in sl list explored all possible paths.
Say, there is an 'if' block after iter_destroy.
The push_stack/pop_stack logic is done as a stack to make sure
that the last 'if' has to be explored before earlier 'if' blocks are checked.
The existing bpf iter logic violated that.
To fix that we need to logically do:

if (is_iter_next_insn(env, insn_idx))
  if (states_equal(env, &sl->state, cur)) {
   if (iter_state->iter.state == BPF_ITER_STATE_DRAINED)
     goto hit;

instead of BPF_ITER_STATE_ACTIVE.
In other words we need to process loop iter == 0 case first
all the way till bpf_exit (just like we do right now),
then process loop iter == 1 and let it exit the loop and
go till bpf_exit (hopefully state prune will find equal states
right after exit from the loop).
then process loop iter == 2 and so on.
If there was an 'if' pushed to stack during loop iter == 1
it has to be popped and explored all the way till bpf_exit.

We cannot just replace BPF_ITER_STATE_ACTIVE with DRAINED.
It would still be ACTIVE with sl->state.branches==0 after that
state explored all paths to bpf_exit.





[Index of Archives]     [Linux Samsung SoC]     [Linux Rockchip SoC]     [Linux Actions SoC]     [Linux for Synopsys ARC Processors]     [Linux NFS]     [Linux NILFS]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]


  Powered by Linux