mirror of
https://github.com/Fishwaldo/linux-bl808.git
synced 2025-06-17 20:25:19 +00:00
bpf: introduce bounded loops
Allow the verifier to validate the loops by simulating their execution. Exisiting programs have used '#pragma unroll' to unroll the loops by the compiler. Instead let the verifier simulate all iterations of the loop. In order to do that introduce parentage chain of bpf_verifier_state and 'branches' counter for the number of branches left to explore. See more detailed algorithm description in bpf_verifier.h This algorithm borrows the key idea from Edward Cree approach: https://patchwork.ozlabs.org/patch/877222/ Additional state pruning heuristics make such brute force loop walk practical even for large loops. Signed-off-by: Alexei Starovoitov <ast@kernel.org> Acked-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
This commit is contained in:
parent
fb8d251ee2
commit
2589726d12
2 changed files with 181 additions and 13 deletions
|
@ -194,6 +194,53 @@ struct bpf_func_state {
|
|||
struct bpf_verifier_state {
|
||||
/* call stack tracking */
|
||||
struct bpf_func_state *frame[MAX_CALL_FRAMES];
|
||||
struct bpf_verifier_state *parent;
|
||||
/*
|
||||
* 'branches' field is the number of branches left to explore:
|
||||
* 0 - all possible paths from this state reached bpf_exit or
|
||||
* were safely pruned
|
||||
* 1 - at least one path is being explored.
|
||||
* This state hasn't reached bpf_exit
|
||||
* 2 - at least two paths are being explored.
|
||||
* This state is an immediate parent of two children.
|
||||
* One is fallthrough branch with branches==1 and another
|
||||
* state is pushed into stack (to be explored later) also with
|
||||
* branches==1. The parent of this state has branches==1.
|
||||
* The verifier state tree connected via 'parent' pointer looks like:
|
||||
* 1
|
||||
* 1
|
||||
* 2 -> 1 (first 'if' pushed into stack)
|
||||
* 1
|
||||
* 2 -> 1 (second 'if' pushed into stack)
|
||||
* 1
|
||||
* 1
|
||||
* 1 bpf_exit.
|
||||
*
|
||||
* Once do_check() reaches bpf_exit, it calls update_branch_counts()
|
||||
* and the verifier state tree will look:
|
||||
* 1
|
||||
* 1
|
||||
* 2 -> 1 (first 'if' pushed into stack)
|
||||
* 1
|
||||
* 1 -> 1 (second 'if' pushed into stack)
|
||||
* 0
|
||||
* 0
|
||||
* 0 bpf_exit.
|
||||
* After pop_stack() the do_check() will resume at second 'if'.
|
||||
*
|
||||
* If is_state_visited() sees a state with branches > 0 it means
|
||||
* there is a loop. If such state is exactly equal to the current state
|
||||
* it's an infinite loop. Note states_equal() checks for states
|
||||
* equvalency, so two states being 'states_equal' does not mean
|
||||
* infinite loop. The exact comparison is provided by
|
||||
* states_maybe_looping() function. It's a stronger pre-check and
|
||||
* much faster than states_equal().
|
||||
*
|
||||
* This algorithm may not find all possible infinite loops or
|
||||
* loop iteration count may be too high.
|
||||
* In such cases BPF_COMPLEXITY_LIMIT_INSNS limit kicks in.
|
||||
*/
|
||||
u32 branches;
|
||||
u32 insn_idx;
|
||||
u32 curframe;
|
||||
u32 active_spin_lock;
|
||||
|
@ -312,7 +359,9 @@ struct bpf_verifier_env {
|
|||
} cfg;
|
||||
u32 subprog_cnt;
|
||||
/* number of instructions analyzed by the verifier */
|
||||
u32 insn_processed;
|
||||
u32 prev_insn_processed, insn_processed;
|
||||
/* number of jmps, calls, exits analyzed so far */
|
||||
u32 prev_jmps_processed, jmps_processed;
|
||||
/* total verification time */
|
||||
u64 verification_time;
|
||||
/* maximum number of verifier states kept in 'branching' instructions */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue