aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/bpf/verifier.c
diff options
context:
space:
mode:
authorDaniel Borkmann <daniel@iogearbox.net>2016-09-07 19:03:42 -0400
committerDavid S. Miller <davem@davemloft.net>2016-09-08 20:28:37 -0400
commit2d2be8cab26ed918e94d2deae89580003242a123 (patch)
tree78371d83d8f4f43a4c24268b93bbbbcb3813d901 /kernel/bpf/verifier.c
parent9f5afeae51526b3ad7b7cb21ee8b145ce6ea7a7a (diff)
bpf: fix range propagation on direct packet access
LLVM can generate code that tests for direct packet access via skb->data/data_end in a way that currently gets rejected by the verifier, example: [...] 7: (61) r3 = *(u32 *)(r6 +80) 8: (61) r9 = *(u32 *)(r6 +76) 9: (bf) r2 = r9 10: (07) r2 += 54 11: (3d) if r3 >= r2 goto pc+12 R1=inv R2=pkt(id=0,off=54,r=0) R3=pkt_end R4=inv R6=ctx R9=pkt(id=0,off=0,r=0) R10=fp 12: (18) r4 = 0xffffff7a 14: (05) goto pc+430 [...] from 11 to 24: R1=inv R2=pkt(id=0,off=54,r=0) R3=pkt_end R4=inv R6=ctx R9=pkt(id=0,off=0,r=0) R10=fp 24: (7b) *(u64 *)(r10 -40) = r1 25: (b7) r1 = 0 26: (63) *(u32 *)(r6 +56) = r1 27: (b7) r2 = 40 28: (71) r8 = *(u8 *)(r9 +20) invalid access to packet, off=20 size=1, R9(id=0,off=0,r=0) The reason why this gets rejected despite a proper test is that we currently call find_good_pkt_pointers() only in case where we detect tests like rX > pkt_end, where rX is of type pkt(id=Y,off=Z,r=0) and derived, for example, from a register of type pkt(id=Y,off=0,r=0) pointing to skb->data. find_good_pkt_pointers() then fills the range in the current branch to pkt(id=Y,off=0,r=Z) on success. For above case, we need to extend that to recognize pkt_end >= rX pattern and mark the other branch that is taken on success with the appropriate pkt(id=Y,off=0,r=Z) type via find_good_pkt_pointers(). Since eBPF operates on BPF_JGT (>) and BPF_JGE (>=), these are the only two practical options to test for from what LLVM could have generated, since there's no such thing as BPF_JLT (<) or BPF_JLE (<=) that we would need to take into account as well. After the fix: [...] 7: (61) r3 = *(u32 *)(r6 +80) 8: (61) r9 = *(u32 *)(r6 +76) 9: (bf) r2 = r9 10: (07) r2 += 54 11: (3d) if r3 >= r2 goto pc+12 R1=inv R2=pkt(id=0,off=54,r=0) R3=pkt_end R4=inv R6=ctx R9=pkt(id=0,off=0,r=0) R10=fp 12: (18) r4 = 0xffffff7a 14: (05) goto pc+430 [...] from 11 to 24: R1=inv R2=pkt(id=0,off=54,r=54) R3=pkt_end R4=inv R6=ctx R9=pkt(id=0,off=0,r=54) R10=fp 24: (7b) *(u64 *)(r10 -40) = r1 25: (b7) r1 = 0 26: (63) *(u32 *)(r6 +56) = r1 27: (b7) r2 = 40 28: (71) r8 = *(u8 *)(r9 +20) 29: (bf) r1 = r8 30: (25) if r8 > 0x3c goto pc+47 R1=inv56 R2=imm40 R3=pkt_end R4=inv R6=ctx R8=inv56 R9=pkt(id=0,off=0,r=54) R10=fp 31: (b7) r1 = 1 [...] Verifier test cases are also added in this work, one that demonstrates the mentioned example here and one that tries a bad packet access for the current/fall-through branch (the one with types pkt(id=X,off=Y,r=0), pkt(id=X,off=0,r=0)), then a case with good and bad accesses, and two with both test variants (>, >=). Fixes: 969bf05eb3ce ("bpf: direct packet access") Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'kernel/bpf/verifier.c')
-rw-r--r--kernel/bpf/verifier.c55
1 files changed, 40 insertions, 15 deletions
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index 48c2705db22c..90493a66dddd 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -1637,21 +1637,42 @@ static int check_alu_op(struct verifier_env *env, struct bpf_insn *insn)
1637 return 0; 1637 return 0;
1638} 1638}
1639 1639
1640static void find_good_pkt_pointers(struct verifier_env *env, 1640static void find_good_pkt_pointers(struct verifier_state *state,
1641 struct reg_state *dst_reg) 1641 const struct reg_state *dst_reg)
1642{ 1642{
1643 struct verifier_state *state = &env->cur_state;
1644 struct reg_state *regs = state->regs, *reg; 1643 struct reg_state *regs = state->regs, *reg;
1645 int i; 1644 int i;
1646 /* r2 = r3; 1645
1647 * r2 += 8 1646 /* LLVM can generate two kind of checks:
1648 * if (r2 > pkt_end) goto somewhere 1647 *
1649 * r2 == dst_reg, pkt_end == src_reg, 1648 * Type 1:
1650 * r2=pkt(id=n,off=8,r=0) 1649 *
1651 * r3=pkt(id=n,off=0,r=0) 1650 * r2 = r3;
1652 * find register r3 and mark its range as r3=pkt(id=n,off=0,r=8) 1651 * r2 += 8;
1653 * so that range of bytes [r3, r3 + 8) is safe to access 1652 * if (r2 > pkt_end) goto <handle exception>
1653 * <access okay>
1654 *
1655 * Where:
1656 * r2 == dst_reg, pkt_end == src_reg
1657 * r2=pkt(id=n,off=8,r=0)
1658 * r3=pkt(id=n,off=0,r=0)
1659 *
1660 * Type 2:
1661 *
1662 * r2 = r3;
1663 * r2 += 8;
1664 * if (pkt_end >= r2) goto <access okay>
1665 * <handle exception>
1666 *
1667 * Where:
1668 * pkt_end == dst_reg, r2 == src_reg
1669 * r2=pkt(id=n,off=8,r=0)
1670 * r3=pkt(id=n,off=0,r=0)
1671 *
1672 * Find register r3 and mark its range as r3=pkt(id=n,off=0,r=8)
1673 * so that range of bytes [r3, r3 + 8) is safe to access.
1654 */ 1674 */
1675
1655 for (i = 0; i < MAX_BPF_REG; i++) 1676 for (i = 0; i < MAX_BPF_REG; i++)
1656 if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id) 1677 if (regs[i].type == PTR_TO_PACKET && regs[i].id == dst_reg->id)
1657 regs[i].range = dst_reg->off; 1678 regs[i].range = dst_reg->off;
@@ -1668,8 +1689,8 @@ static void find_good_pkt_pointers(struct verifier_env *env,
1668static int check_cond_jmp_op(struct verifier_env *env, 1689static int check_cond_jmp_op(struct verifier_env *env,
1669 struct bpf_insn *insn, int *insn_idx) 1690 struct bpf_insn *insn, int *insn_idx)
1670{ 1691{
1671 struct reg_state *regs = env->cur_state.regs, *dst_reg; 1692 struct verifier_state *other_branch, *this_branch = &env->cur_state;
1672 struct verifier_state *other_branch; 1693 struct reg_state *regs = this_branch->regs, *dst_reg;
1673 u8 opcode = BPF_OP(insn->code); 1694 u8 opcode = BPF_OP(insn->code);
1674 int err; 1695 int err;
1675 1696
@@ -1750,13 +1771,17 @@ static int check_cond_jmp_op(struct verifier_env *env,
1750 } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT && 1771 } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGT &&
1751 dst_reg->type == PTR_TO_PACKET && 1772 dst_reg->type == PTR_TO_PACKET &&
1752 regs[insn->src_reg].type == PTR_TO_PACKET_END) { 1773 regs[insn->src_reg].type == PTR_TO_PACKET_END) {
1753 find_good_pkt_pointers(env, dst_reg); 1774 find_good_pkt_pointers(this_branch, dst_reg);
1775 } else if (BPF_SRC(insn->code) == BPF_X && opcode == BPF_JGE &&
1776 dst_reg->type == PTR_TO_PACKET_END &&
1777 regs[insn->src_reg].type == PTR_TO_PACKET) {
1778 find_good_pkt_pointers(other_branch, &regs[insn->src_reg]);
1754 } else if (is_pointer_value(env, insn->dst_reg)) { 1779 } else if (is_pointer_value(env, insn->dst_reg)) {
1755 verbose("R%d pointer comparison prohibited\n", insn->dst_reg); 1780 verbose("R%d pointer comparison prohibited\n", insn->dst_reg);
1756 return -EACCES; 1781 return -EACCES;
1757 } 1782 }
1758 if (log_level) 1783 if (log_level)
1759 print_verifier_state(&env->cur_state); 1784 print_verifier_state(this_branch);
1760 return 0; 1785 return 0;
1761} 1786}
1762 1787