diff options
Diffstat (limited to 'kernel/bpf/verifier.c')
-rw-r--r-- | kernel/bpf/verifier.c | 65 |
1 files changed, 51 insertions, 14 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 8b8d6ba39e23..c48ca2a34b5e 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
@@ -1116,7 +1116,12 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn | |||
1116 | /* ctx accesses must be at a fixed offset, so that we can | 1116 | /* ctx accesses must be at a fixed offset, so that we can |
1117 | * determine what type of data were returned. | 1117 | * determine what type of data were returned. |
1118 | */ | 1118 | */ |
1119 | if (!tnum_is_const(reg->var_off)) { | 1119 | if (reg->off) { |
1120 | verbose("dereference of modified ctx ptr R%d off=%d+%d, ctx+const is allowed, ctx+const+const is not\n", | ||
1121 | regno, reg->off, off - reg->off); | ||
1122 | return -EACCES; | ||
1123 | } | ||
1124 | if (!tnum_is_const(reg->var_off) || reg->var_off.value) { | ||
1120 | char tn_buf[48]; | 1125 | char tn_buf[48]; |
1121 | 1126 | ||
1122 | tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); | 1127 | tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); |
@@ -1124,7 +1129,6 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn | |||
1124 | tn_buf, off, size); | 1129 | tn_buf, off, size); |
1125 | return -EACCES; | 1130 | return -EACCES; |
1126 | } | 1131 | } |
1127 | off += reg->var_off.value; | ||
1128 | err = check_ctx_access(env, insn_idx, off, size, t, ®_type); | 1132 | err = check_ctx_access(env, insn_idx, off, size, t, ®_type); |
1129 | if (!err && t == BPF_READ && value_regno >= 0) { | 1133 | if (!err && t == BPF_READ && value_regno >= 0) { |
1130 | /* ctx access returns either a scalar, or a | 1134 | /* ctx access returns either a scalar, or a |
@@ -2426,12 +2430,15 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) | |||
2426 | } | 2430 | } |
2427 | 2431 | ||
2428 | static void find_good_pkt_pointers(struct bpf_verifier_state *state, | 2432 | static void find_good_pkt_pointers(struct bpf_verifier_state *state, |
2429 | struct bpf_reg_state *dst_reg) | 2433 | struct bpf_reg_state *dst_reg, |
2434 | bool range_right_open) | ||
2430 | { | 2435 | { |
2431 | struct bpf_reg_state *regs = state->regs, *reg; | 2436 | struct bpf_reg_state *regs = state->regs, *reg; |
2437 | u16 new_range; | ||
2432 | int i; | 2438 | int i; |
2433 | 2439 | ||
2434 | if (dst_reg->off < 0) | 2440 | if (dst_reg->off < 0 || |
2441 | (dst_reg->off == 0 && range_right_open)) | ||
2435 | /* This doesn't give us any range */ | 2442 | /* This doesn't give us any range */ |
2436 | return; | 2443 | return; |
2437 | 2444 | ||
@@ -2442,9 +2449,13 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, | |||
2442 | */ | 2449 | */ |
2443 | return; | 2450 | return; |
2444 | 2451 | ||
2445 | /* LLVM can generate four kind of checks: | 2452 | new_range = dst_reg->off; |
2453 | if (range_right_open) | ||
2454 | new_range--; | ||
2455 | |||
2456 | /* Examples for register markings: | ||
2446 | * | 2457 | * |
2447 | * Type 1/2: | 2458 | * pkt_data in dst register: |
2448 | * | 2459 | * |
2449 | * r2 = r3; | 2460 | * r2 = r3; |
2450 | * r2 += 8; | 2461 | * r2 += 8; |
@@ -2461,7 +2472,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, | |||
2461 | * r2=pkt(id=n,off=8,r=0) | 2472 | * r2=pkt(id=n,off=8,r=0) |
2462 | * r3=pkt(id=n,off=0,r=0) | 2473 | * r3=pkt(id=n,off=0,r=0) |
2463 | * | 2474 | * |
2464 | * Type 3/4: | 2475 | * pkt_data in src register: |
2465 | * | 2476 | * |
2466 | * r2 = r3; | 2477 | * r2 = r3; |
2467 | * r2 += 8; | 2478 | * r2 += 8; |
@@ -2479,7 +2490,9 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, | |||
2479 | * r3=pkt(id=n,off=0,r=0) | 2490 | * r3=pkt(id=n,off=0,r=0) |
2480 | * | 2491 | * |
2481 | * Find register r3 and mark its range as r3=pkt(id=n,off=0,r=8) | 2492 | * Find register r3 and mark its range as r3=pkt(id=n,off=0,r=8) |
2482 | * so that range of bytes [r3, r3 + 8) is safe to access. | 2493 | * or r3=pkt(id=n,off=0,r=8-1), so that range of bytes [r3, r3 + 8) |
2494 | * and [r3, r3 + 8-1) respectively is safe to access depending on | ||
2495 | * the check. | ||
2483 | */ | 2496 | */ |
2484 | 2497 | ||
2485 | /* If our ids match, then we must have the same max_value. And we | 2498 | /* If our ids match, then we must have the same max_value. And we |
@@ -2490,14 +2503,14 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, | |||
2490 | for (i = 0; i < MAX_BPF_REG; i++) | 2503 | for (i = 0; i < MAX_BPF_REG; i++) |
2491 | if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) | 2504 | if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) |
2492 | /* keep the maximum range already checked */ | 2505 | /* keep the maximum range already checked */ |
2493 | regs[i].range = max_t(u16, regs[i].range, dst_reg->off); | 2506 | regs[i].range = max(regs[i].range, new_range); |
2494 | 2507 | ||
2495 | for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { | 2508 | for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { |
2496 | if (state->stack_slot_type[i] != STACK_SPILL) | 2509 | if (state->stack_slot_type[i] != STACK_SPILL) |
2497 | continue; | 2510 | continue; |
2498 | reg = &state->spilled_regs[i / BPF_REG_SIZE]; | 2511 | reg = &state->spilled_regs[i / BPF_REG_SIZE]; |
2499 | if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id) | 2512 | if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id) |
2500 | reg->range = max_t(u16, reg->range, dst_reg->off); | 2513 | reg->range = max(reg->range, new_range); |
2501 | } | 2514 | } |
2502 | } | 2515 | } |
2503 | 2516 | ||
@@ -2861,19 +2874,43 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env, | |||
2861 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && | 2874 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && |
2862 | dst_reg->type == PTR_TO_PACKET && | 2875 | dst_reg->type == PTR_TO_PACKET && |
2863 | regs[insn->src_reg].type == PTR_TO_PACKET_END) { | 2876 | regs[insn->src_reg].type == PTR_TO_PACKET_END) { |
2864 | find_good_pkt_pointers(this_branch, dst_reg); | 2877 | /* pkt_data' > pkt_end */ |
2878 | find_good_pkt_pointers(this_branch, dst_reg, false); | ||
2879 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && | ||
2880 | dst_reg->type == PTR_TO_PACKET_END && | ||
2881 | regs[insn->src_reg].type == PTR_TO_PACKET) { | ||
2882 | /* pkt_end > pkt_data' */ | ||
2883 | find_good_pkt_pointers(other_branch, ®s[insn->src_reg], true); | ||
2865 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT && | 2884 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT && |
2866 | dst_reg->type == PTR_TO_PACKET && | 2885 | dst_reg->type == PTR_TO_PACKET && |
2867 | regs[insn->src_reg].type == PTR_TO_PACKET_END) { | 2886 | regs[insn->src_reg].type == PTR_TO_PACKET_END) { |
2868 | find_good_pkt_pointers(other_branch, dst_reg); | 2887 | /* pkt_data' < pkt_end */ |
2888 | find_good_pkt_pointers(other_branch, dst_reg, true); | ||
2889 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT && | ||
2890 | dst_reg->type == PTR_TO_PACKET_END && | ||
2891 | regs[insn->src_reg].type == PTR_TO_PACKET) { | ||
2892 | /* pkt_end < pkt_data' */ | ||
2893 | find_good_pkt_pointers(this_branch, ®s[insn->src_reg], false); | ||
2894 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE && | ||
2895 | dst_reg->type == PTR_TO_PACKET && | ||
2896 | regs[insn->src_reg].type == PTR_TO_PACKET_END) { | ||
2897 | /* pkt_data' >= pkt_end */ | ||
2898 | find_good_pkt_pointers(this_branch, dst_reg, true); | ||
2869 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE && | 2899 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE && |
2870 | dst_reg->type == PTR_TO_PACKET_END && | 2900 | dst_reg->type == PTR_TO_PACKET_END && |
2871 | regs[insn->src_reg].type == PTR_TO_PACKET) { | 2901 | regs[insn->src_reg].type == PTR_TO_PACKET) { |
2872 | find_good_pkt_pointers(other_branch, ®s[insn->src_reg]); | 2902 | /* pkt_end >= pkt_data' */ |
2903 | find_good_pkt_pointers(other_branch, ®s[insn->src_reg], false); | ||
2904 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE && | ||
2905 | dst_reg->type == PTR_TO_PACKET && | ||
2906 | regs[insn->src_reg].type == PTR_TO_PACKET_END) { | ||
2907 | /* pkt_data' <= pkt_end */ | ||
2908 | find_good_pkt_pointers(other_branch, dst_reg, false); | ||
2873 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE && | 2909 | } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE && |
2874 | dst_reg->type == PTR_TO_PACKET_END && | 2910 | dst_reg->type == PTR_TO_PACKET_END && |
2875 | regs[insn->src_reg].type == PTR_TO_PACKET) { | 2911 | regs[insn->src_reg].type == PTR_TO_PACKET) { |
2876 | find_good_pkt_pointers(this_branch, ®s[insn->src_reg]); | 2912 | /* pkt_end <= pkt_data' */ |
2913 | find_good_pkt_pointers(this_branch, ®s[insn->src_reg], true); | ||
2877 | } else if (is_pointer_value(env, insn->dst_reg)) { | 2914 | } else if (is_pointer_value(env, insn->dst_reg)) { |
2878 | verbose("R%d pointer comparison prohibited\n", insn->dst_reg); | 2915 | verbose("R%d pointer comparison prohibited\n", insn->dst_reg); |
2879 | return -EACCES; | 2916 | return -EACCES; |