bpf: fix pattern matches for direct packet access
authorDaniel Borkmann <daniel@iogearbox.net>
Sat, 21 Oct 2017 00:34:22 +0000 (02:34 +0200)
committerDavid S. Miller <davem@davemloft.net>
Sat, 21 Oct 2017 23:56:09 +0000 (00:56 +0100)
Alexander had a test program with direct packet access, where
the access test was in the form of data + X > data_end. In an
unrelated change to the program LLVM decided to swap the branches
and emitted code for the test in form of data + X <= data_end.
We hadn't seen these being generated previously, thus verifier
would reject the program. Therefore, fix up the verifier to
detect all test cases, so we don't run into such issues in the
future.

Fixes: b4e432f1000a ("bpf: enable BPF_J{LT, LE, SLT, SLE} opcodes in verifier")
Reported-by: Alexander Alemayhu <alexander@alemayhu.com>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Acked-by: John Fastabend <john.fastabend@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
kernel/bpf/verifier.c

index 49cb5ad147466a9652a21f24bfa93bd45c93643e..c48ca2a34b5e131420f4795c4a0eaf9d9a64861d 100644 (file)
@@ -2874,18 +2874,42 @@ static int check_cond_jmp_op(struct bpf_verifier_env *env,
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
                   dst_reg->type == PTR_TO_PACKET &&
                   regs[insn->src_reg].type == PTR_TO_PACKET_END) {
+               /* pkt_data' > pkt_end */
                find_good_pkt_pointers(this_branch, dst_reg, false);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
+                  dst_reg->type == PTR_TO_PACKET_END &&
+                  regs[insn->src_reg].type == PTR_TO_PACKET) {
+               /* pkt_end > pkt_data' */
+               find_good_pkt_pointers(other_branch, &regs[insn->src_reg], true);
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
                   dst_reg->type == PTR_TO_PACKET &&
                   regs[insn->src_reg].type == PTR_TO_PACKET_END) {
+               /* pkt_data' < pkt_end */
                find_good_pkt_pointers(other_branch, dst_reg, true);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLT &&
+                  dst_reg->type == PTR_TO_PACKET_END &&
+                  regs[insn->src_reg].type == PTR_TO_PACKET) {
+               /* pkt_end < pkt_data' */
+               find_good_pkt_pointers(this_branch, &regs[insn->src_reg], false);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
+                  dst_reg->type == PTR_TO_PACKET &&
+                  regs[insn->src_reg].type == PTR_TO_PACKET_END) {
+               /* pkt_data' >= pkt_end */
+               find_good_pkt_pointers(this_branch, dst_reg, true);
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
                   dst_reg->type == PTR_TO_PACKET_END &&
                   regs[insn->src_reg].type == PTR_TO_PACKET) {
+               /* pkt_end >= pkt_data' */
                find_good_pkt_pointers(other_branch, &regs[insn->src_reg], false);
+       } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
+                  dst_reg->type == PTR_TO_PACKET &&
+                  regs[insn->src_reg].type == PTR_TO_PACKET_END) {
+               /* pkt_data' <= pkt_end */
+               find_good_pkt_pointers(other_branch, dst_reg, false);
        } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JLE &&
                   dst_reg->type == PTR_TO_PACKET_END &&
                   regs[insn->src_reg].type == PTR_TO_PACKET) {
+               /* pkt_end <= pkt_data' */
                find_good_pkt_pointers(this_branch, &regs[insn->src_reg], true);
        } else if (is_pointer_value(env, insn->dst_reg)) {
                verbose("R%d pointer comparison prohibited\n", insn->dst_reg);