Skip to content

Commit fb2a311

Browse files
borkmanndavem330
authored andcommitted
bpf: fix off by one for range markings with L{T, E} patterns
During review I noticed that the current logic for direct packet access marking in check_cond_jmp_op() has an off by one for the upper right range border when marking in find_good_pkt_pointers() with BPF_JLT and BPF_JLE. It's not really harmful given access up to pkt_end is always safe, but we should nevertheless correct the range marking before it becomes ABI. If pkt_data' denotes a pkt_data derived pointer (pkt_data + X), then for pkt_data' < pkt_end in the true branch as well as for pkt_end <= pkt_data' in the false branch we mark the range with X although it should really be X - 1 in these cases. For example, X could be pkt_end - pkt_data, then when testing for pkt_data' < pkt_end the verifier simulation cannot deduce that a byte load of pkt_data' - 1 would succeed in this branch. Fixes: b4e432f ("bpf: enable BPF_J{LT, LE, SLT, SLE} opcodes in verifier") Signed-off-by: Daniel Borkmann <[email protected]> Acked-by: Alexei Starovoitov <[email protected]> Acked-by: John Fastabend <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 8695a53 commit fb2a311

File tree

1 file changed

+21
-12
lines changed

1 file changed

+21
-12
lines changed

kernel/bpf/verifier.c

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2430,12 +2430,15 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn)
24302430
}
24312431

24322432
static void find_good_pkt_pointers(struct bpf_verifier_state *state,
2433-
struct bpf_reg_state *dst_reg)
2433+
struct bpf_reg_state *dst_reg,
2434+
bool range_right_open)
24342435
{
24352436
struct bpf_reg_state *regs = state->regs, *reg;
2437+
u16 new_range;
24362438
int i;
24372439

2438-
if (dst_reg->off < 0)
2440+
if (dst_reg->off < 0 ||
2441+
(dst_reg->off == 0 && range_right_open))
24392442
/* This doesn't give us any range */
24402443
return;
24412444

@@ -2446,9 +2449,13 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
24462449
*/
24472450
return;
24482451

2449-
/* 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:
24502457
*
2451-
* Type 1/2:
2458+
* pkt_data in dst register:
24522459
*
24532460
* r2 = r3;
24542461
* r2 += 8;
@@ -2465,7 +2472,7 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
24652472
* r2=pkt(id=n,off=8,r=0)
24662473
* r3=pkt(id=n,off=0,r=0)
24672474
*
2468-
* Type 3/4:
2475+
* pkt_data in src register:
24692476
*
24702477
* r2 = r3;
24712478
* r2 += 8;
@@ -2483,7 +2490,9 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
24832490
* r3=pkt(id=n,off=0,r=0)
24842491
*
24852492
* Find register r3 and mark its range as r3=pkt(id=n,off=0,r=8)
2486-
* 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.
24872496
*/
24882497

24892498
/* If our ids match, then we must have the same max_value. And we
@@ -2494,14 +2503,14 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state,
24942503
for (i = 0; i < MAX_BPF_REG; i++)
24952504
if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id)
24962505
/* keep the maximum range already checked */
2497-
regs[i].range = max_t(u16, regs[i].range, dst_reg->off);
2506+
regs[i].range = max(regs[i].range, new_range);
24982507

24992508
for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) {
25002509
if (state->stack_slot_type[i] != STACK_SPILL)
25012510
continue;
25022511
reg = &state->spilled_regs[i / BPF_REG_SIZE];
25032512
if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id)
2504-
reg->range = max_t(u16, reg->range, dst_reg->off);
2513+
reg->range = max(reg->range, new_range);
25052514
}
25062515
}
25072516

@@ -2865,19 +2874,19 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
28652874
} else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
28662875
dst_reg->type == PTR_TO_PACKET &&
28672876
regs[insn->src_reg].type == PTR_TO_PACKET_END) {
2868-
find_good_pkt_pointers(this_branch, dst_reg);
2877+
find_good_pkt_pointers(this_branch, dst_reg, false);
28692878
} else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
28702879
dst_reg->type == PTR_TO_PACKET &&
28712880
regs[insn->src_reg].type == PTR_TO_PACKET_END) {
2872-
find_good_pkt_pointers(other_branch, dst_reg);
2881+
find_good_pkt_pointers(other_branch, dst_reg, true);
28732882
} else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
28742883
dst_reg->type == PTR_TO_PACKET_END &&
28752884
regs[insn->src_reg].type == PTR_TO_PACKET) {
2876-
find_good_pkt_pointers(other_branch, &regs[insn->src_reg]);
2885+
find_good_pkt_pointers(other_branch, &regs[insn->src_reg], false);
28772886
} else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
28782887
dst_reg->type == PTR_TO_PACKET_END &&
28792888
regs[insn->src_reg].type == PTR_TO_PACKET) {
2880-
find_good_pkt_pointers(this_branch, &regs[insn->src_reg]);
2889+
find_good_pkt_pointers(this_branch, &regs[insn->src_reg], true);
28812890
} else if (is_pointer_value(env, insn->dst_reg)) {
28822891
verbose("R%d pointer comparison prohibited\n", insn->dst_reg);
28832892
return -EACCES;

0 commit comments

Comments
 (0)