aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2016-04-05 16:33:17 -0400
committerDavid S. Miller <davem@davemloft.net>2016-04-08 16:16:42 -0400
commit07016151a446d25397b24588df4ed5cf777a69bb (patch)
treea71bc4074fc0bb4cdb8302ca12bf43a30fba244b
parent1fc2257e837f86c2688fdcc5c8810b73c133794d (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.c9
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;