diff options
author | Daniel Borkmann <daniel@iogearbox.net> | 2016-04-05 16:33:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-04-08 16:16:42 -0400 |
commit | 07016151a446d25397b24588df4ed5cf777a69bb (patch) | |
tree | a71bc4074fc0bb4cdb8302ca12bf43a30fba244b | |
parent | 1fc2257e837f86c2688fdcc5c8810b73c133794d (diff) |
bpf, verifier: further improve search pruning
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 f1bca824dabb ("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: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | kernel/bpf/verifier.c | 9 |
1 files changed, 7 insertions, 2 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 58792fed5678..8233021538d3 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
@@ -202,6 +202,9 @@ struct verifier_env { | |||
202 | bool allow_ptr_leaks; | 202 | bool allow_ptr_leaks; |
203 | }; | 203 | }; |
204 | 204 | ||
205 | #define BPF_COMPLEXITY_LIMIT_INSNS 65536 | ||
206 | #define BPF_COMPLEXITY_LIMIT_STACK 1024 | ||
207 | |||
205 | /* verbose verifier prints what it's seeing | 208 | /* verbose verifier prints what it's seeing |
206 | * bpf_check() is called under lock, so no race to access these global vars | 209 | * bpf_check() is called under lock, so no race to access these global vars |
207 | */ | 210 | */ |
@@ -454,7 +457,7 @@ static struct verifier_state *push_stack(struct verifier_env *env, int insn_idx, | |||
454 | elem->next = env->head; | 457 | elem->next = env->head; |
455 | env->head = elem; | 458 | env->head = elem; |
456 | env->stack_size++; | 459 | env->stack_size++; |
457 | if (env->stack_size > 1024) { | 460 | if (env->stack_size > BPF_COMPLEXITY_LIMIT_STACK) { |
458 | verbose("BPF program is too complex\n"); | 461 | verbose("BPF program is too complex\n"); |
459 | goto err; | 462 | goto err; |
460 | } | 463 | } |
@@ -1543,6 +1546,8 @@ peek_stack: | |||
1543 | goto peek_stack; | 1546 | goto peek_stack; |
1544 | else if (ret < 0) | 1547 | else if (ret < 0) |
1545 | goto err_free; | 1548 | goto err_free; |
1549 | if (t + 1 < insn_cnt) | ||
1550 | env->explored_states[t + 1] = STATE_LIST_MARK; | ||
1546 | } else if (opcode == BPF_JA) { | 1551 | } else if (opcode == BPF_JA) { |
1547 | if (BPF_SRC(insns[t].code) != BPF_K) { | 1552 | if (BPF_SRC(insns[t].code) != BPF_K) { |
1548 | ret = -EINVAL; | 1553 | ret = -EINVAL; |
@@ -1747,7 +1752,7 @@ static int do_check(struct verifier_env *env) | |||
1747 | insn = &insns[insn_idx]; | 1752 | insn = &insns[insn_idx]; |
1748 | class = BPF_CLASS(insn->code); | 1753 | class = BPF_CLASS(insn->code); |
1749 | 1754 | ||
1750 | if (++insn_processed > 32768) { | 1755 | if (++insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) { |
1751 | verbose("BPF program is too large. Proccessed %d insn\n", | 1756 | verbose("BPF program is too large. Proccessed %d insn\n", |
1752 | insn_processed); | 1757 | insn_processed); |
1753 | return -E2BIG; | 1758 | return -E2BIG; |