diff options
author | Alexei Starovoitov <ast@fb.com> | 2017-03-24 18:57:33 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2017-03-24 23:51:28 -0400 |
commit | b1977682a3858b5584ffea7cfb7bd863f68db18d (patch) | |
tree | f255d32b2a4e2b72811b36bbd04b27ef9fd2a947 | |
parent | 43a6684519ab0a6c52024b5e25322476cabad893 (diff) |
bpf: improve verifier packet range checks
llvm can optimize the 'if (ptr > data_end)' checks to be in the order
slightly different than the original C code which will confuse verifier.
Like:
if (ptr + 16 > data_end)
return TC_ACT_SHOT;
// may be followed by
if (ptr + 14 > data_end)
return TC_ACT_SHOT;
while llvm can see that 'ptr' is valid for all 16 bytes,
the verifier could not.
Fix verifier logic to account for such case and add a test.
Reported-by: Huapeng Zhou <hzhou@fb.com>
Fixes: 969bf05eb3ce ("bpf: direct packet access")
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Martin KaFai Lau <kafai@fb.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | kernel/bpf/verifier.c | 5 | ||||
-rw-r--r-- | tools/testing/selftests/bpf/test_verifier.c | 20 |
2 files changed, 23 insertions, 2 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 796b68d00119..5e6202e62265 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c | |||
@@ -1973,14 +1973,15 @@ static void find_good_pkt_pointers(struct bpf_verifier_state *state, | |||
1973 | 1973 | ||
1974 | for (i = 0; i < MAX_BPF_REG; i++) | 1974 | for (i = 0; i < MAX_BPF_REG; i++) |
1975 | if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) | 1975 | if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) |
1976 | regs[i].range = dst_reg->off; | 1976 | /* keep the maximum range already checked */ |
1977 | regs[i].range = max(regs[i].range, dst_reg->off); | ||
1977 | 1978 | ||
1978 | for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { | 1979 | for (i = 0; i < MAX_BPF_STACK; i += BPF_REG_SIZE) { |
1979 | if (state->stack_slot_type[i] != STACK_SPILL) | 1980 | if (state->stack_slot_type[i] != STACK_SPILL) |
1980 | continue; | 1981 | continue; |
1981 | reg = &state->spilled_regs[i / BPF_REG_SIZE]; | 1982 | reg = &state->spilled_regs[i / BPF_REG_SIZE]; |
1982 | if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id) | 1983 | if (reg->type == PTR_TO_PACKET && reg->id == dst_reg->id) |
1983 | reg->range = dst_reg->off; | 1984 | reg->range = max(reg->range, dst_reg->off); |
1984 | } | 1985 | } |
1985 | } | 1986 | } |
1986 | 1987 | ||
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c index d1555e4240c0..7d761d4cc759 100644 --- a/tools/testing/selftests/bpf/test_verifier.c +++ b/tools/testing/selftests/bpf/test_verifier.c | |||
@@ -3418,6 +3418,26 @@ static struct bpf_test tests[] = { | |||
3418 | .prog_type = BPF_PROG_TYPE_LWT_XMIT, | 3418 | .prog_type = BPF_PROG_TYPE_LWT_XMIT, |
3419 | }, | 3419 | }, |
3420 | { | 3420 | { |
3421 | "overlapping checks for direct packet access", | ||
3422 | .insns = { | ||
3423 | BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, | ||
3424 | offsetof(struct __sk_buff, data)), | ||
3425 | BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, | ||
3426 | offsetof(struct __sk_buff, data_end)), | ||
3427 | BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), | ||
3428 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), | ||
3429 | BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 4), | ||
3430 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_2), | ||
3431 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 6), | ||
3432 | BPF_JMP_REG(BPF_JGT, BPF_REG_1, BPF_REG_3, 1), | ||
3433 | BPF_LDX_MEM(BPF_H, BPF_REG_0, BPF_REG_2, 6), | ||
3434 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
3435 | BPF_EXIT_INSN(), | ||
3436 | }, | ||
3437 | .result = ACCEPT, | ||
3438 | .prog_type = BPF_PROG_TYPE_LWT_XMIT, | ||
3439 | }, | ||
3440 | { | ||
3421 | "invalid access of tc_classid for LWT_IN", | 3441 | "invalid access of tc_classid for LWT_IN", |
3422 | .insns = { | 3442 | .insns = { |
3423 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, | 3443 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, |