• Daniel Borkmann's avatar
    bpf, verifier: further improve search pruning · 07016151
    Daniel Borkmann authored
    The verifier needs to go through every path of the program in
    order to check that it terminates safely, which can be quite a
    lot of instructions that need to be processed f.e. in cases with
    more branchy programs. With search pruning from f1bca824 ("bpf:
    add search pruning optimization to verifier") the search space can
    already be reduced significantly when the verifier detects that
    a previously walked path with same register and stack contents
    terminated already (see verifier's states_equal()), so the search
    can skip walking those states.
    
    When working with larger programs of > ~2000 (out of max 4096)
    insns, we found that the current limit of 32k instructions is easily
    hit. For example, a case we ran into is that the search space cannot
    be pruned due to branches at the beginning of the program that make
    use of certain stack space slots (STACK_MISC), which are never used
    in the remaining program (STACK_INVALID). Therefore, the verifier
    needs to walk paths for the slots in STACK_INVALID state, but also
    all remaining paths with a stack structure, where the slots are in
    STACK_MISC, which can nearly double the search space needed. After
    various experiments, we find that a limit of 64k processed insns is
    a more reasonable choice when dealing with larger programs in practice.
    This still allows to reject extreme crafted cases that can have a
    much higher complexity (f.e. > ~300k) within the 4096 insns limit
    due to search pruning not being able to take effect.
    
    Furthermore, we found that a lot of states can be pruned after a
    call instruction, f.e. we were able to reduce the search state by
    ~35% in some cases with this heuristic, trade-off is to keep a bit
    more states in env->explored_states. Usually, call instructions
    have a number of preceding register assignments and/or stack stores,
    where search pruning has a better chance to suceed in states_equal()
    test. The current code marks the branch targets with STATE_LIST_MARK
    in case of conditional jumps, and the next (t + 1) instruction in
    case of unconditional jump so that f.e. a backjump will walk it. We
    also did experiments with using t + insns[t].off + 1 as a marker in
    the unconditionally jump case instead of t + 1 with the rationale
    that these two branches of execution that converge after the label
    might have more potential of pruning. We found that it was a bit
    better, but not necessarily significantly better than the current
    state, perhaps also due to clang not generating back jumps often.
    Hence, we left that as is for now.
    Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
    Acked-by: default avatarAlexei Starovoitov <ast@kernel.org>
    Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
    07016151
verifier.c 63.8 KB