Commit a92a72a2 authored by Daniel Borkmann's avatar Daniel Borkmann

Merge branch 'bpf-verifier-resilience'

Alexei Starovoitov says:

====================
Three patches to improve verifier ability to handle pathological bpf
programs with a lot of branches:

- make sure prog_load syscall can be aborted
- improve branch taken analysis
- introduce per-insn complexity limit for unprivileged programs
====================
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parents 025dceb0 ceefbc96
...@@ -175,6 +175,7 @@ struct bpf_verifier_stack_elem { ...@@ -175,6 +175,7 @@ struct bpf_verifier_stack_elem {
#define BPF_COMPLEXITY_LIMIT_INSNS 131072 #define BPF_COMPLEXITY_LIMIT_INSNS 131072
#define BPF_COMPLEXITY_LIMIT_STACK 1024 #define BPF_COMPLEXITY_LIMIT_STACK 1024
#define BPF_COMPLEXITY_LIMIT_STATES 64
#define BPF_MAP_PTR_UNPRIV 1UL #define BPF_MAP_PTR_UNPRIV 1UL
#define BPF_MAP_PTR_POISON ((void *)((0xeB9FUL << 1) + \ #define BPF_MAP_PTR_POISON ((void *)((0xeB9FUL << 1) + \
...@@ -3751,6 +3752,79 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate, ...@@ -3751,6 +3752,79 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *vstate,
} }
} }
/* compute branch direction of the expression "if (reg opcode val) goto target;"
* and return:
* 1 - branch will be taken and "goto target" will be executed
* 0 - branch will not be taken and fall-through to next insn
* -1 - unknown. Example: "if (reg < 5)" is unknown when register value range [0,10]
*/
static int is_branch_taken(struct bpf_reg_state *reg, u64 val, u8 opcode)
{
if (__is_pointer_value(false, reg))
return -1;
switch (opcode) {
case BPF_JEQ:
if (tnum_is_const(reg->var_off))
return !!tnum_equals_const(reg->var_off, val);
break;
case BPF_JNE:
if (tnum_is_const(reg->var_off))
return !tnum_equals_const(reg->var_off, val);
break;
case BPF_JGT:
if (reg->umin_value > val)
return 1;
else if (reg->umax_value <= val)
return 0;
break;
case BPF_JSGT:
if (reg->smin_value > (s64)val)
return 1;
else if (reg->smax_value < (s64)val)
return 0;
break;
case BPF_JLT:
if (reg->umax_value < val)
return 1;
else if (reg->umin_value >= val)
return 0;
break;
case BPF_JSLT:
if (reg->smax_value < (s64)val)
return 1;
else if (reg->smin_value >= (s64)val)
return 0;
break;
case BPF_JGE:
if (reg->umin_value >= val)
return 1;
else if (reg->umax_value < val)
return 0;
break;
case BPF_JSGE:
if (reg->smin_value >= (s64)val)
return 1;
else if (reg->smax_value < (s64)val)
return 0;
break;
case BPF_JLE:
if (reg->umax_value <= val)
return 1;
else if (reg->umin_value > val)
return 0;
break;
case BPF_JSLE:
if (reg->smax_value <= (s64)val)
return 1;
else if (reg->smin_value > (s64)val)
return 0;
break;
}
return -1;
}
/* Adjusts the register min/max values in the case that the dst_reg is the /* Adjusts the register min/max values in the case that the dst_reg is the
* variable register that we are working on, and src_reg is a constant or we're * variable register that we are working on, and src_reg is a constant or we're
* simply doing a BPF_K check. * simply doing a BPF_K check.
...@@ -4152,21 +4226,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, ...@@ -4152,21 +4226,15 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
dst_reg = &regs[insn->dst_reg]; dst_reg = &regs[insn->dst_reg];
/* detect if R == 0 where R was initialized to zero earlier */ if (BPF_SRC(insn->code) == BPF_K) {
if (BPF_SRC(insn->code) == BPF_K && int pred = is_branch_taken(dst_reg, insn->imm, opcode);
(opcode == BPF_JEQ || opcode == BPF_JNE) &&
dst_reg->type == SCALAR_VALUE && if (pred == 1) {
tnum_is_const(dst_reg->var_off)) { /* only follow the goto, ignore fall-through */
if ((opcode == BPF_JEQ && dst_reg->var_off.value == insn->imm) ||
(opcode == BPF_JNE && dst_reg->var_off.value != insn->imm)) {
/* if (imm == imm) goto pc+off;
* only follow the goto, ignore fall-through
*/
*insn_idx += insn->off; *insn_idx += insn->off;
return 0; return 0;
} else { } else if (pred == 0) {
/* if (imm != imm) goto pc+off; /* only follow fall-through branch, since
* only follow fall-through branch, since
* that's where the program will go * that's where the program will go
*/ */
return 0; return 0;
...@@ -4980,7 +5048,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) ...@@ -4980,7 +5048,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
struct bpf_verifier_state_list *new_sl; struct bpf_verifier_state_list *new_sl;
struct bpf_verifier_state_list *sl; struct bpf_verifier_state_list *sl;
struct bpf_verifier_state *cur = env->cur_state, *new; struct bpf_verifier_state *cur = env->cur_state, *new;
int i, j, err; int i, j, err, states_cnt = 0;
sl = env->explored_states[insn_idx]; sl = env->explored_states[insn_idx];
if (!sl) if (!sl)
...@@ -5007,8 +5075,12 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) ...@@ -5007,8 +5075,12 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
return 1; return 1;
} }
sl = sl->next; sl = sl->next;
states_cnt++;
} }
if (!env->allow_ptr_leaks && states_cnt > BPF_COMPLEXITY_LIMIT_STATES)
return 0;
/* there were no equivalent states, remember current one. /* there were no equivalent states, remember current one.
* technically the current state is not proven to be safe yet, * technically the current state is not proven to be safe yet,
* but it will either reach outer most bpf_exit (which means it's safe) * but it will either reach outer most bpf_exit (which means it's safe)
...@@ -5148,6 +5220,9 @@ static int do_check(struct bpf_verifier_env *env) ...@@ -5148,6 +5220,9 @@ static int do_check(struct bpf_verifier_env *env)
goto process_bpf_exit; goto process_bpf_exit;
} }
if (signal_pending(current))
return -EAGAIN;
if (need_resched()) if (need_resched())
cond_resched(); cond_resched();
......
...@@ -8576,7 +8576,7 @@ static struct bpf_test tests[] = { ...@@ -8576,7 +8576,7 @@ static struct bpf_test tests[] = {
BPF_JMP_IMM(BPF_JA, 0, 0, -7), BPF_JMP_IMM(BPF_JA, 0, 0, -7),
}, },
.fixup_map_hash_8b = { 4 }, .fixup_map_hash_8b = { 4 },
.errstr = "R0 invalid mem access 'inv'", .errstr = "unbounded min value",
.result = REJECT, .result = REJECT,
}, },
{ {
...@@ -10547,7 +10547,7 @@ static struct bpf_test tests[] = { ...@@ -10547,7 +10547,7 @@ static struct bpf_test tests[] = {
"check deducing bounds from const, 5", "check deducing bounds from const, 5",
.insns = { .insns = {
BPF_MOV64_IMM(BPF_REG_0, 0), BPF_MOV64_IMM(BPF_REG_0, 0),
BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 0, 1), BPF_JMP_IMM(BPF_JSGE, BPF_REG_0, 1, 1),
BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_1), BPF_ALU64_REG(BPF_SUB, BPF_REG_0, BPF_REG_1),
BPF_EXIT_INSN(), BPF_EXIT_INSN(),
}, },
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment