diff options
author | Daniel Borkmann <daniel@iogearbox.net> | 2016-09-07 19:03:42 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2016-09-08 20:28:37 -0400 |
commit | 2d2be8cab26ed918e94d2deae89580003242a123 (patch) | |
tree | 78371d83d8f4f43a4c24268b93bbbbcb3813d901 /samples/bpf | |
parent | 9f5afeae51526b3ad7b7cb21ee8b145ce6ea7a7a (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.c | 102 |
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, |