aboutsummaryrefslogtreecommitdiffstats
path: root/samples/bpf
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 /samples/bpf
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 'samples/bpf')
-rw-r--r--samples/bpf/test_verifier.c102
1 files changed, 102 insertions, 0 deletions
diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c
index 78c6f131d94f..1f6cc9b6a23b 100644
--- a/samples/bpf/test_verifier.c
+++ b/samples/bpf/test_verifier.c
@@ -1529,6 +1529,108 @@ static struct bpf_test tests[] = {
1529 .prog_type = BPF_PROG_TYPE_SCHED_CLS, 1529 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
1530 }, 1530 },
1531 { 1531 {
1532 "direct packet access: test5 (pkt_end >= reg, good access)",
1533 .insns = {
1534 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
1535 offsetof(struct __sk_buff, data)),
1536 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
1537 offsetof(struct __sk_buff, data_end)),
1538 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1539 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1540 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 2),
1541 BPF_MOV64_IMM(BPF_REG_0, 1),
1542 BPF_EXIT_INSN(),
1543 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1544 BPF_MOV64_IMM(BPF_REG_0, 0),
1545 BPF_EXIT_INSN(),
1546 },
1547 .result = ACCEPT,
1548 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
1549 },
1550 {
1551 "direct packet access: test6 (pkt_end >= reg, bad access)",
1552 .insns = {
1553 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
1554 offsetof(struct __sk_buff, data)),
1555 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
1556 offsetof(struct __sk_buff, data_end)),
1557 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1558 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1559 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 3),
1560 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1561 BPF_MOV64_IMM(BPF_REG_0, 1),
1562 BPF_EXIT_INSN(),
1563 BPF_MOV64_IMM(BPF_REG_0, 0),
1564 BPF_EXIT_INSN(),
1565 },
1566 .errstr = "invalid access to packet",
1567 .result = REJECT,
1568 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
1569 },
1570 {
1571 "direct packet access: test7 (pkt_end >= reg, both accesses)",
1572 .insns = {
1573 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
1574 offsetof(struct __sk_buff, data)),
1575 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
1576 offsetof(struct __sk_buff, data_end)),
1577 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1578 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1579 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 3),
1580 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1581 BPF_MOV64_IMM(BPF_REG_0, 1),
1582 BPF_EXIT_INSN(),
1583 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1584 BPF_MOV64_IMM(BPF_REG_0, 0),
1585 BPF_EXIT_INSN(),
1586 },
1587 .errstr = "invalid access to packet",
1588 .result = REJECT,
1589 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
1590 },
1591 {
1592 "direct packet access: test8 (double test, variant 1)",
1593 .insns = {
1594 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
1595 offsetof(struct __sk_buff, data)),
1596 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
1597 offsetof(struct __sk_buff, data_end)),
1598 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1599 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1600 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 4),
1601 BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
1602 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1603 BPF_MOV64_IMM(BPF_REG_0, 1),
1604 BPF_EXIT_INSN(),
1605 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1606 BPF_MOV64_IMM(BPF_REG_0, 0),
1607 BPF_EXIT_INSN(),
1608 },
1609 .result = ACCEPT,
1610 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
1611 },
1612 {
1613 "direct packet access: test9 (double test, variant 2)",
1614 .insns = {
1615 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
1616 offsetof(struct __sk_buff, data)),
1617 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
1618 offsetof(struct __sk_buff, data_end)),
1619 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
1620 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8),
1621 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 2),
1622 BPF_MOV64_IMM(BPF_REG_0, 1),
1623 BPF_EXIT_INSN(),
1624 BPF_JMP_REG(BPF_JGT, BPF_REG_0, BPF_REG_3, 1),
1625 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1626 BPF_LDX_MEM(BPF_B, BPF_REG_0, BPF_REG_2, 0),
1627 BPF_MOV64_IMM(BPF_REG_0, 0),
1628 BPF_EXIT_INSN(),
1629 },
1630 .result = ACCEPT,
1631 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
1632 },
1633 {
1532 "helper access to packet: test1, valid packet_ptr range", 1634 "helper access to packet: test1, valid packet_ptr range",
1533 .insns = { 1635 .insns = {
1534 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, 1636 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,