diff options
author | Alexei Starovoitov <ast@plumgrid.com> | 2015-10-08 01:23:23 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2015-10-12 22:13:37 -0400 |
commit | bf5088773faffc4a052b95aa978a1660bf5f3f8d (patch) | |
tree | ce888633783880b3970d2270fc1428fbbb84259e /samples/bpf | |
parent | aaac3ba95e4c8b496d22f68bd1bc01cfbf525eca (diff) |
bpf: add unprivileged bpf tests
Add new tests samples/bpf/test_verifier:
unpriv: return pointer
checks that pointer cannot be returned from the eBPF program
unpriv: add const to pointer
unpriv: add pointer to pointer
unpriv: neg pointer
checks that pointer arithmetic is disallowed
unpriv: cmp pointer with const
unpriv: cmp pointer with pointer
checks that comparison of pointers is disallowed
Only one case allowed 'void *value = bpf_map_lookup_elem(..); if (value == 0) ...'
unpriv: check that printk is disallowed
since bpf_trace_printk is not available to unprivileged
unpriv: pass pointer to helper function
checks that pointers cannot be passed to functions that expect integers
If function expects a pointer the verifier allows only that type of pointer.
Like 1st argument of bpf_map_lookup_elem() must be pointer to map.
(applies to non-root as well)
unpriv: indirectly pass pointer on stack to helper function
checks that pointer stored into stack cannot be used as part of key
passed into bpf_map_lookup_elem()
unpriv: mangle pointer on stack 1
unpriv: mangle pointer on stack 2
checks that writing into stack slot that already contains a pointer
is disallowed
unpriv: read pointer from stack in small chunks
checks that < 8 byte read from stack slot that contains a pointer is
disallowed
unpriv: write pointer into ctx
checks that storing pointers into skb->fields is disallowed
unpriv: write pointer into map elem value
checks that storing pointers into element values is disallowed
For example:
int bpf_prog(struct __sk_buff *skb)
{
u32 key = 0;
u64 *value = bpf_map_lookup_elem(&map, &key);
if (value)
*value = (u64) skb;
}
will be rejected.
unpriv: partial copy of pointer
checks that doing 32-bit register mov from register containing
a pointer is disallowed
unpriv: pass pointer to tail_call
checks that passing pointer as an index into bpf_tail_call
is disallowed
unpriv: cmp map pointer with zero
checks that comparing map pointer with constant is disallowed
unpriv: write into frame pointer
checks that frame pointer is read-only (applies to root too)
unpriv: cmp of frame pointer
checks that R10 cannot be using in comparison
unpriv: cmp of stack pointer
checks that Rx = R10 - imm is ok, but comparing Rx is not
unpriv: obfuscate stack pointer
checks that Rx = R10 - imm is ok, but Rx -= imm is not
Signed-off-by: Alexei Starovoitov <ast@plumgrid.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'samples/bpf')
-rw-r--r-- | samples/bpf/libbpf.h | 8 | ||||
-rw-r--r-- | samples/bpf/test_verifier.c | 357 |
2 files changed, 355 insertions, 10 deletions
diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h index 7235e292a03b..b7f63c70b4a2 100644 --- a/samples/bpf/libbpf.h +++ b/samples/bpf/libbpf.h | |||
@@ -64,6 +64,14 @@ extern char bpf_log_buf[LOG_BUF_SIZE]; | |||
64 | .off = 0, \ | 64 | .off = 0, \ |
65 | .imm = 0 }) | 65 | .imm = 0 }) |
66 | 66 | ||
67 | #define BPF_MOV32_REG(DST, SRC) \ | ||
68 | ((struct bpf_insn) { \ | ||
69 | .code = BPF_ALU | BPF_MOV | BPF_X, \ | ||
70 | .dst_reg = DST, \ | ||
71 | .src_reg = SRC, \ | ||
72 | .off = 0, \ | ||
73 | .imm = 0 }) | ||
74 | |||
67 | /* Short form of mov, dst_reg = imm32 */ | 75 | /* Short form of mov, dst_reg = imm32 */ |
68 | 76 | ||
69 | #define BPF_MOV64_IMM(DST, IMM) \ | 77 | #define BPF_MOV64_IMM(DST, IMM) \ |
diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c index ee0f110c9c54..563c507c0a09 100644 --- a/samples/bpf/test_verifier.c +++ b/samples/bpf/test_verifier.c | |||
@@ -15,20 +15,27 @@ | |||
15 | #include <string.h> | 15 | #include <string.h> |
16 | #include <linux/filter.h> | 16 | #include <linux/filter.h> |
17 | #include <stddef.h> | 17 | #include <stddef.h> |
18 | #include <stdbool.h> | ||
19 | #include <sys/resource.h> | ||
18 | #include "libbpf.h" | 20 | #include "libbpf.h" |
19 | 21 | ||
20 | #define MAX_INSNS 512 | 22 | #define MAX_INSNS 512 |
21 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) | 23 | #define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x))) |
22 | 24 | ||
25 | #define MAX_FIXUPS 8 | ||
26 | |||
23 | struct bpf_test { | 27 | struct bpf_test { |
24 | const char *descr; | 28 | const char *descr; |
25 | struct bpf_insn insns[MAX_INSNS]; | 29 | struct bpf_insn insns[MAX_INSNS]; |
26 | int fixup[32]; | 30 | int fixup[MAX_FIXUPS]; |
31 | int prog_array_fixup[MAX_FIXUPS]; | ||
27 | const char *errstr; | 32 | const char *errstr; |
33 | const char *errstr_unpriv; | ||
28 | enum { | 34 | enum { |
35 | UNDEF, | ||
29 | ACCEPT, | 36 | ACCEPT, |
30 | REJECT | 37 | REJECT |
31 | } result; | 38 | } result, result_unpriv; |
32 | enum bpf_prog_type prog_type; | 39 | enum bpf_prog_type prog_type; |
33 | }; | 40 | }; |
34 | 41 | ||
@@ -96,6 +103,7 @@ static struct bpf_test tests[] = { | |||
96 | BPF_EXIT_INSN(), | 103 | BPF_EXIT_INSN(), |
97 | }, | 104 | }, |
98 | .errstr = "invalid BPF_LD_IMM insn", | 105 | .errstr = "invalid BPF_LD_IMM insn", |
106 | .errstr_unpriv = "R1 pointer comparison", | ||
99 | .result = REJECT, | 107 | .result = REJECT, |
100 | }, | 108 | }, |
101 | { | 109 | { |
@@ -109,6 +117,7 @@ static struct bpf_test tests[] = { | |||
109 | BPF_EXIT_INSN(), | 117 | BPF_EXIT_INSN(), |
110 | }, | 118 | }, |
111 | .errstr = "invalid BPF_LD_IMM insn", | 119 | .errstr = "invalid BPF_LD_IMM insn", |
120 | .errstr_unpriv = "R1 pointer comparison", | ||
112 | .result = REJECT, | 121 | .result = REJECT, |
113 | }, | 122 | }, |
114 | { | 123 | { |
@@ -219,6 +228,7 @@ static struct bpf_test tests[] = { | |||
219 | BPF_EXIT_INSN(), | 228 | BPF_EXIT_INSN(), |
220 | }, | 229 | }, |
221 | .errstr = "R0 !read_ok", | 230 | .errstr = "R0 !read_ok", |
231 | .errstr_unpriv = "R1 pointer comparison", | ||
222 | .result = REJECT, | 232 | .result = REJECT, |
223 | }, | 233 | }, |
224 | { | 234 | { |
@@ -294,7 +304,9 @@ static struct bpf_test tests[] = { | |||
294 | BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), | 304 | BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), |
295 | BPF_EXIT_INSN(), | 305 | BPF_EXIT_INSN(), |
296 | }, | 306 | }, |
307 | .errstr_unpriv = "R0 leaks addr", | ||
297 | .result = ACCEPT, | 308 | .result = ACCEPT, |
309 | .result_unpriv = REJECT, | ||
298 | }, | 310 | }, |
299 | { | 311 | { |
300 | "check corrupted spill/fill", | 312 | "check corrupted spill/fill", |
@@ -310,6 +322,7 @@ static struct bpf_test tests[] = { | |||
310 | 322 | ||
311 | BPF_EXIT_INSN(), | 323 | BPF_EXIT_INSN(), |
312 | }, | 324 | }, |
325 | .errstr_unpriv = "attempt to corrupt spilled", | ||
313 | .errstr = "corrupted spill", | 326 | .errstr = "corrupted spill", |
314 | .result = REJECT, | 327 | .result = REJECT, |
315 | }, | 328 | }, |
@@ -473,6 +486,7 @@ static struct bpf_test tests[] = { | |||
473 | }, | 486 | }, |
474 | .fixup = {3}, | 487 | .fixup = {3}, |
475 | .errstr = "R0 invalid mem access", | 488 | .errstr = "R0 invalid mem access", |
489 | .errstr_unpriv = "R0 leaks addr", | ||
476 | .result = REJECT, | 490 | .result = REJECT, |
477 | }, | 491 | }, |
478 | { | 492 | { |
@@ -495,6 +509,8 @@ static struct bpf_test tests[] = { | |||
495 | BPF_MOV64_IMM(BPF_REG_0, 0), | 509 | BPF_MOV64_IMM(BPF_REG_0, 0), |
496 | BPF_EXIT_INSN(), | 510 | BPF_EXIT_INSN(), |
497 | }, | 511 | }, |
512 | .errstr_unpriv = "R1 pointer comparison", | ||
513 | .result_unpriv = REJECT, | ||
498 | .result = ACCEPT, | 514 | .result = ACCEPT, |
499 | }, | 515 | }, |
500 | { | 516 | { |
@@ -521,6 +537,8 @@ static struct bpf_test tests[] = { | |||
521 | BPF_MOV64_IMM(BPF_REG_0, 0), | 537 | BPF_MOV64_IMM(BPF_REG_0, 0), |
522 | BPF_EXIT_INSN(), | 538 | BPF_EXIT_INSN(), |
523 | }, | 539 | }, |
540 | .errstr_unpriv = "R1 pointer comparison", | ||
541 | .result_unpriv = REJECT, | ||
524 | .result = ACCEPT, | 542 | .result = ACCEPT, |
525 | }, | 543 | }, |
526 | { | 544 | { |
@@ -555,6 +573,8 @@ static struct bpf_test tests[] = { | |||
555 | BPF_EXIT_INSN(), | 573 | BPF_EXIT_INSN(), |
556 | }, | 574 | }, |
557 | .fixup = {24}, | 575 | .fixup = {24}, |
576 | .errstr_unpriv = "R1 pointer comparison", | ||
577 | .result_unpriv = REJECT, | ||
558 | .result = ACCEPT, | 578 | .result = ACCEPT, |
559 | }, | 579 | }, |
560 | { | 580 | { |
@@ -603,6 +623,8 @@ static struct bpf_test tests[] = { | |||
603 | BPF_MOV64_IMM(BPF_REG_0, 0), | 623 | BPF_MOV64_IMM(BPF_REG_0, 0), |
604 | BPF_EXIT_INSN(), | 624 | BPF_EXIT_INSN(), |
605 | }, | 625 | }, |
626 | .errstr_unpriv = "R1 pointer comparison", | ||
627 | .result_unpriv = REJECT, | ||
606 | .result = ACCEPT, | 628 | .result = ACCEPT, |
607 | }, | 629 | }, |
608 | { | 630 | { |
@@ -642,6 +664,8 @@ static struct bpf_test tests[] = { | |||
642 | BPF_MOV64_IMM(BPF_REG_0, 0), | 664 | BPF_MOV64_IMM(BPF_REG_0, 0), |
643 | BPF_EXIT_INSN(), | 665 | BPF_EXIT_INSN(), |
644 | }, | 666 | }, |
667 | .errstr_unpriv = "R1 pointer comparison", | ||
668 | .result_unpriv = REJECT, | ||
645 | .result = ACCEPT, | 669 | .result = ACCEPT, |
646 | }, | 670 | }, |
647 | { | 671 | { |
@@ -699,6 +723,7 @@ static struct bpf_test tests[] = { | |||
699 | }, | 723 | }, |
700 | .fixup = {4}, | 724 | .fixup = {4}, |
701 | .errstr = "different pointers", | 725 | .errstr = "different pointers", |
726 | .errstr_unpriv = "R1 pointer comparison", | ||
702 | .result = REJECT, | 727 | .result = REJECT, |
703 | }, | 728 | }, |
704 | { | 729 | { |
@@ -720,6 +745,7 @@ static struct bpf_test tests[] = { | |||
720 | }, | 745 | }, |
721 | .fixup = {6}, | 746 | .fixup = {6}, |
722 | .errstr = "different pointers", | 747 | .errstr = "different pointers", |
748 | .errstr_unpriv = "R1 pointer comparison", | ||
723 | .result = REJECT, | 749 | .result = REJECT, |
724 | }, | 750 | }, |
725 | { | 751 | { |
@@ -742,6 +768,7 @@ static struct bpf_test tests[] = { | |||
742 | }, | 768 | }, |
743 | .fixup = {7}, | 769 | .fixup = {7}, |
744 | .errstr = "different pointers", | 770 | .errstr = "different pointers", |
771 | .errstr_unpriv = "R1 pointer comparison", | ||
745 | .result = REJECT, | 772 | .result = REJECT, |
746 | }, | 773 | }, |
747 | { | 774 | { |
@@ -752,6 +779,7 @@ static struct bpf_test tests[] = { | |||
752 | BPF_EXIT_INSN(), | 779 | BPF_EXIT_INSN(), |
753 | }, | 780 | }, |
754 | .errstr = "invalid bpf_context access", | 781 | .errstr = "invalid bpf_context access", |
782 | .errstr_unpriv = "R1 leaks addr", | ||
755 | .result = REJECT, | 783 | .result = REJECT, |
756 | }, | 784 | }, |
757 | { | 785 | { |
@@ -762,6 +790,7 @@ static struct bpf_test tests[] = { | |||
762 | BPF_EXIT_INSN(), | 790 | BPF_EXIT_INSN(), |
763 | }, | 791 | }, |
764 | .errstr = "invalid bpf_context access", | 792 | .errstr = "invalid bpf_context access", |
793 | .errstr_unpriv = "R1 leaks addr", | ||
765 | .result = REJECT, | 794 | .result = REJECT, |
766 | }, | 795 | }, |
767 | { | 796 | { |
@@ -772,16 +801,18 @@ static struct bpf_test tests[] = { | |||
772 | BPF_EXIT_INSN(), | 801 | BPF_EXIT_INSN(), |
773 | }, | 802 | }, |
774 | .errstr = "invalid bpf_context access", | 803 | .errstr = "invalid bpf_context access", |
804 | .errstr_unpriv = "R1 leaks addr", | ||
775 | .result = REJECT, | 805 | .result = REJECT, |
776 | }, | 806 | }, |
777 | { | 807 | { |
778 | "check out of range skb->cb access", | 808 | "check out of range skb->cb access", |
779 | .insns = { | 809 | .insns = { |
780 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, | 810 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, |
781 | offsetof(struct __sk_buff, cb[60])), | 811 | offsetof(struct __sk_buff, cb[0]) + 256), |
782 | BPF_EXIT_INSN(), | 812 | BPF_EXIT_INSN(), |
783 | }, | 813 | }, |
784 | .errstr = "invalid bpf_context access", | 814 | .errstr = "invalid bpf_context access", |
815 | .errstr_unpriv = "", | ||
785 | .result = REJECT, | 816 | .result = REJECT, |
786 | .prog_type = BPF_PROG_TYPE_SCHED_ACT, | 817 | .prog_type = BPF_PROG_TYPE_SCHED_ACT, |
787 | }, | 818 | }, |
@@ -803,6 +834,8 @@ static struct bpf_test tests[] = { | |||
803 | BPF_EXIT_INSN(), | 834 | BPF_EXIT_INSN(), |
804 | }, | 835 | }, |
805 | .result = ACCEPT, | 836 | .result = ACCEPT, |
837 | .errstr_unpriv = "R1 leaks addr", | ||
838 | .result_unpriv = REJECT, | ||
806 | }, | 839 | }, |
807 | { | 840 | { |
808 | "write skb fields from tc_cls_act prog", | 841 | "write skb fields from tc_cls_act prog", |
@@ -819,6 +852,8 @@ static struct bpf_test tests[] = { | |||
819 | offsetof(struct __sk_buff, cb[3])), | 852 | offsetof(struct __sk_buff, cb[3])), |
820 | BPF_EXIT_INSN(), | 853 | BPF_EXIT_INSN(), |
821 | }, | 854 | }, |
855 | .errstr_unpriv = "", | ||
856 | .result_unpriv = REJECT, | ||
822 | .result = ACCEPT, | 857 | .result = ACCEPT, |
823 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, | 858 | .prog_type = BPF_PROG_TYPE_SCHED_CLS, |
824 | }, | 859 | }, |
@@ -881,6 +916,270 @@ static struct bpf_test tests[] = { | |||
881 | .result = REJECT, | 916 | .result = REJECT, |
882 | .errstr = "invalid stack off=0 size=8", | 917 | .errstr = "invalid stack off=0 size=8", |
883 | }, | 918 | }, |
919 | { | ||
920 | "unpriv: return pointer", | ||
921 | .insns = { | ||
922 | BPF_MOV64_REG(BPF_REG_0, BPF_REG_10), | ||
923 | BPF_EXIT_INSN(), | ||
924 | }, | ||
925 | .result = ACCEPT, | ||
926 | .result_unpriv = REJECT, | ||
927 | .errstr_unpriv = "R0 leaks addr", | ||
928 | }, | ||
929 | { | ||
930 | "unpriv: add const to pointer", | ||
931 | .insns = { | ||
932 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8), | ||
933 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
934 | BPF_EXIT_INSN(), | ||
935 | }, | ||
936 | .result = ACCEPT, | ||
937 | .result_unpriv = REJECT, | ||
938 | .errstr_unpriv = "R1 pointer arithmetic", | ||
939 | }, | ||
940 | { | ||
941 | "unpriv: add pointer to pointer", | ||
942 | .insns = { | ||
943 | BPF_ALU64_REG(BPF_ADD, BPF_REG_1, BPF_REG_10), | ||
944 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
945 | BPF_EXIT_INSN(), | ||
946 | }, | ||
947 | .result = ACCEPT, | ||
948 | .result_unpriv = REJECT, | ||
949 | .errstr_unpriv = "R1 pointer arithmetic", | ||
950 | }, | ||
951 | { | ||
952 | "unpriv: neg pointer", | ||
953 | .insns = { | ||
954 | BPF_ALU64_IMM(BPF_NEG, BPF_REG_1, 0), | ||
955 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
956 | BPF_EXIT_INSN(), | ||
957 | }, | ||
958 | .result = ACCEPT, | ||
959 | .result_unpriv = REJECT, | ||
960 | .errstr_unpriv = "R1 pointer arithmetic", | ||
961 | }, | ||
962 | { | ||
963 | "unpriv: cmp pointer with const", | ||
964 | .insns = { | ||
965 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0), | ||
966 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
967 | BPF_EXIT_INSN(), | ||
968 | }, | ||
969 | .result = ACCEPT, | ||
970 | .result_unpriv = REJECT, | ||
971 | .errstr_unpriv = "R1 pointer comparison", | ||
972 | }, | ||
973 | { | ||
974 | "unpriv: cmp pointer with pointer", | ||
975 | .insns = { | ||
976 | BPF_JMP_REG(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0), | ||
977 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
978 | BPF_EXIT_INSN(), | ||
979 | }, | ||
980 | .result = ACCEPT, | ||
981 | .result_unpriv = REJECT, | ||
982 | .errstr_unpriv = "R10 pointer comparison", | ||
983 | }, | ||
984 | { | ||
985 | "unpriv: check that printk is disallowed", | ||
986 | .insns = { | ||
987 | BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), | ||
988 | BPF_MOV64_REG(BPF_REG_1, BPF_REG_10), | ||
989 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8), | ||
990 | BPF_MOV64_IMM(BPF_REG_2, 8), | ||
991 | BPF_MOV64_REG(BPF_REG_3, BPF_REG_1), | ||
992 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_trace_printk), | ||
993 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
994 | BPF_EXIT_INSN(), | ||
995 | }, | ||
996 | .errstr_unpriv = "unknown func 6", | ||
997 | .result_unpriv = REJECT, | ||
998 | .result = ACCEPT, | ||
999 | }, | ||
1000 | { | ||
1001 | "unpriv: pass pointer to helper function", | ||
1002 | .insns = { | ||
1003 | BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), | ||
1004 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
1005 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | ||
1006 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
1007 | BPF_MOV64_REG(BPF_REG_3, BPF_REG_2), | ||
1008 | BPF_MOV64_REG(BPF_REG_4, BPF_REG_2), | ||
1009 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_update_elem), | ||
1010 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1011 | BPF_EXIT_INSN(), | ||
1012 | }, | ||
1013 | .fixup = {3}, | ||
1014 | .errstr_unpriv = "R4 leaks addr", | ||
1015 | .result_unpriv = REJECT, | ||
1016 | .result = ACCEPT, | ||
1017 | }, | ||
1018 | { | ||
1019 | "unpriv: indirectly pass pointer on stack to helper function", | ||
1020 | .insns = { | ||
1021 | BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), | ||
1022 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
1023 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | ||
1024 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
1025 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), | ||
1026 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1027 | BPF_EXIT_INSN(), | ||
1028 | }, | ||
1029 | .fixup = {3}, | ||
1030 | .errstr = "invalid indirect read from stack off -8+0 size 8", | ||
1031 | .result = REJECT, | ||
1032 | }, | ||
1033 | { | ||
1034 | "unpriv: mangle pointer on stack 1", | ||
1035 | .insns = { | ||
1036 | BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), | ||
1037 | BPF_ST_MEM(BPF_W, BPF_REG_10, -8, 0), | ||
1038 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1039 | BPF_EXIT_INSN(), | ||
1040 | }, | ||
1041 | .errstr_unpriv = "attempt to corrupt spilled", | ||
1042 | .result_unpriv = REJECT, | ||
1043 | .result = ACCEPT, | ||
1044 | }, | ||
1045 | { | ||
1046 | "unpriv: mangle pointer on stack 2", | ||
1047 | .insns = { | ||
1048 | BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), | ||
1049 | BPF_ST_MEM(BPF_B, BPF_REG_10, -1, 0), | ||
1050 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1051 | BPF_EXIT_INSN(), | ||
1052 | }, | ||
1053 | .errstr_unpriv = "attempt to corrupt spilled", | ||
1054 | .result_unpriv = REJECT, | ||
1055 | .result = ACCEPT, | ||
1056 | }, | ||
1057 | { | ||
1058 | "unpriv: read pointer from stack in small chunks", | ||
1059 | .insns = { | ||
1060 | BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_10, -8), | ||
1061 | BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_10, -8), | ||
1062 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1063 | BPF_EXIT_INSN(), | ||
1064 | }, | ||
1065 | .errstr = "invalid size", | ||
1066 | .result = REJECT, | ||
1067 | }, | ||
1068 | { | ||
1069 | "unpriv: write pointer into ctx", | ||
1070 | .insns = { | ||
1071 | BPF_STX_MEM(BPF_DW, BPF_REG_1, BPF_REG_1, 0), | ||
1072 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1073 | BPF_EXIT_INSN(), | ||
1074 | }, | ||
1075 | .errstr_unpriv = "R1 leaks addr", | ||
1076 | .result_unpriv = REJECT, | ||
1077 | .errstr = "invalid bpf_context access", | ||
1078 | .result = REJECT, | ||
1079 | }, | ||
1080 | { | ||
1081 | "unpriv: write pointer into map elem value", | ||
1082 | .insns = { | ||
1083 | BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0), | ||
1084 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
1085 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | ||
1086 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
1087 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem), | ||
1088 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1), | ||
1089 | BPF_STX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 0), | ||
1090 | BPF_EXIT_INSN(), | ||
1091 | }, | ||
1092 | .fixup = {3}, | ||
1093 | .errstr_unpriv = "R0 leaks addr", | ||
1094 | .result_unpriv = REJECT, | ||
1095 | .result = ACCEPT, | ||
1096 | }, | ||
1097 | { | ||
1098 | "unpriv: partial copy of pointer", | ||
1099 | .insns = { | ||
1100 | BPF_MOV32_REG(BPF_REG_1, BPF_REG_10), | ||
1101 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1102 | BPF_EXIT_INSN(), | ||
1103 | }, | ||
1104 | .errstr_unpriv = "R10 partial copy", | ||
1105 | .result_unpriv = REJECT, | ||
1106 | .result = ACCEPT, | ||
1107 | }, | ||
1108 | { | ||
1109 | "unpriv: pass pointer to tail_call", | ||
1110 | .insns = { | ||
1111 | BPF_MOV64_REG(BPF_REG_3, BPF_REG_1), | ||
1112 | BPF_LD_MAP_FD(BPF_REG_2, 0), | ||
1113 | BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_tail_call), | ||
1114 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1115 | BPF_EXIT_INSN(), | ||
1116 | }, | ||
1117 | .prog_array_fixup = {1}, | ||
1118 | .errstr_unpriv = "R3 leaks addr into helper", | ||
1119 | .result_unpriv = REJECT, | ||
1120 | .result = ACCEPT, | ||
1121 | }, | ||
1122 | { | ||
1123 | "unpriv: cmp map pointer with zero", | ||
1124 | .insns = { | ||
1125 | BPF_MOV64_IMM(BPF_REG_1, 0), | ||
1126 | BPF_LD_MAP_FD(BPF_REG_1, 0), | ||
1127 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 0), | ||
1128 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1129 | BPF_EXIT_INSN(), | ||
1130 | }, | ||
1131 | .fixup = {1}, | ||
1132 | .errstr_unpriv = "R1 pointer comparison", | ||
1133 | .result_unpriv = REJECT, | ||
1134 | .result = ACCEPT, | ||
1135 | }, | ||
1136 | { | ||
1137 | "unpriv: write into frame pointer", | ||
1138 | .insns = { | ||
1139 | BPF_MOV64_REG(BPF_REG_10, BPF_REG_1), | ||
1140 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1141 | BPF_EXIT_INSN(), | ||
1142 | }, | ||
1143 | .errstr = "frame pointer is read only", | ||
1144 | .result = REJECT, | ||
1145 | }, | ||
1146 | { | ||
1147 | "unpriv: cmp of frame pointer", | ||
1148 | .insns = { | ||
1149 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_10, 0, 0), | ||
1150 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1151 | BPF_EXIT_INSN(), | ||
1152 | }, | ||
1153 | .errstr_unpriv = "R10 pointer comparison", | ||
1154 | .result_unpriv = REJECT, | ||
1155 | .result = ACCEPT, | ||
1156 | }, | ||
1157 | { | ||
1158 | "unpriv: cmp of stack pointer", | ||
1159 | .insns = { | ||
1160 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
1161 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | ||
1162 | BPF_JMP_IMM(BPF_JEQ, BPF_REG_2, 0, 0), | ||
1163 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1164 | BPF_EXIT_INSN(), | ||
1165 | }, | ||
1166 | .errstr_unpriv = "R2 pointer comparison", | ||
1167 | .result_unpriv = REJECT, | ||
1168 | .result = ACCEPT, | ||
1169 | }, | ||
1170 | { | ||
1171 | "unpriv: obfuscate stack pointer", | ||
1172 | .insns = { | ||
1173 | BPF_MOV64_REG(BPF_REG_2, BPF_REG_10), | ||
1174 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | ||
1175 | BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8), | ||
1176 | BPF_MOV64_IMM(BPF_REG_0, 0), | ||
1177 | BPF_EXIT_INSN(), | ||
1178 | }, | ||
1179 | .errstr_unpriv = "R2 pointer arithmetic", | ||
1180 | .result_unpriv = REJECT, | ||
1181 | .result = ACCEPT, | ||
1182 | }, | ||
884 | }; | 1183 | }; |
885 | 1184 | ||
886 | static int probe_filter_length(struct bpf_insn *fp) | 1185 | static int probe_filter_length(struct bpf_insn *fp) |
@@ -896,13 +1195,24 @@ static int probe_filter_length(struct bpf_insn *fp) | |||
896 | 1195 | ||
897 | static int create_map(void) | 1196 | static int create_map(void) |
898 | { | 1197 | { |
899 | long long key, value = 0; | ||
900 | int map_fd; | 1198 | int map_fd; |
901 | 1199 | ||
902 | map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 1024); | 1200 | map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, |
903 | if (map_fd < 0) { | 1201 | sizeof(long long), sizeof(long long), 1024); |
1202 | if (map_fd < 0) | ||
904 | printf("failed to create map '%s'\n", strerror(errno)); | 1203 | printf("failed to create map '%s'\n", strerror(errno)); |
905 | } | 1204 | |
1205 | return map_fd; | ||
1206 | } | ||
1207 | |||
1208 | static int create_prog_array(void) | ||
1209 | { | ||
1210 | int map_fd; | ||
1211 | |||
1212 | map_fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, | ||
1213 | sizeof(int), sizeof(int), 4); | ||
1214 | if (map_fd < 0) | ||
1215 | printf("failed to create prog_array '%s'\n", strerror(errno)); | ||
906 | 1216 | ||
907 | return map_fd; | 1217 | return map_fd; |
908 | } | 1218 | } |
@@ -910,13 +1220,17 @@ static int create_map(void) | |||
910 | static int test(void) | 1220 | static int test(void) |
911 | { | 1221 | { |
912 | int prog_fd, i, pass_cnt = 0, err_cnt = 0; | 1222 | int prog_fd, i, pass_cnt = 0, err_cnt = 0; |
1223 | bool unpriv = geteuid() != 0; | ||
913 | 1224 | ||
914 | for (i = 0; i < ARRAY_SIZE(tests); i++) { | 1225 | for (i = 0; i < ARRAY_SIZE(tests); i++) { |
915 | struct bpf_insn *prog = tests[i].insns; | 1226 | struct bpf_insn *prog = tests[i].insns; |
916 | int prog_type = tests[i].prog_type; | 1227 | int prog_type = tests[i].prog_type; |
917 | int prog_len = probe_filter_length(prog); | 1228 | int prog_len = probe_filter_length(prog); |
918 | int *fixup = tests[i].fixup; | 1229 | int *fixup = tests[i].fixup; |
919 | int map_fd = -1; | 1230 | int *prog_array_fixup = tests[i].prog_array_fixup; |
1231 | int expected_result; | ||
1232 | const char *expected_errstr; | ||
1233 | int map_fd = -1, prog_array_fd = -1; | ||
920 | 1234 | ||
921 | if (*fixup) { | 1235 | if (*fixup) { |
922 | map_fd = create_map(); | 1236 | map_fd = create_map(); |
@@ -926,13 +1240,31 @@ static int test(void) | |||
926 | fixup++; | 1240 | fixup++; |
927 | } while (*fixup); | 1241 | } while (*fixup); |
928 | } | 1242 | } |
1243 | if (*prog_array_fixup) { | ||
1244 | prog_array_fd = create_prog_array(); | ||
1245 | |||
1246 | do { | ||
1247 | prog[*prog_array_fixup].imm = prog_array_fd; | ||
1248 | prog_array_fixup++; | ||
1249 | } while (*prog_array_fixup); | ||
1250 | } | ||
929 | printf("#%d %s ", i, tests[i].descr); | 1251 | printf("#%d %s ", i, tests[i].descr); |
930 | 1252 | ||
931 | prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER, | 1253 | prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER, |
932 | prog, prog_len * sizeof(struct bpf_insn), | 1254 | prog, prog_len * sizeof(struct bpf_insn), |
933 | "GPL", 0); | 1255 | "GPL", 0); |
934 | 1256 | ||
935 | if (tests[i].result == ACCEPT) { | 1257 | if (unpriv && tests[i].result_unpriv != UNDEF) |
1258 | expected_result = tests[i].result_unpriv; | ||
1259 | else | ||
1260 | expected_result = tests[i].result; | ||
1261 | |||
1262 | if (unpriv && tests[i].errstr_unpriv) | ||
1263 | expected_errstr = tests[i].errstr_unpriv; | ||
1264 | else | ||
1265 | expected_errstr = tests[i].errstr; | ||
1266 | |||
1267 | if (expected_result == ACCEPT) { | ||
936 | if (prog_fd < 0) { | 1268 | if (prog_fd < 0) { |
937 | printf("FAIL\nfailed to load prog '%s'\n", | 1269 | printf("FAIL\nfailed to load prog '%s'\n", |
938 | strerror(errno)); | 1270 | strerror(errno)); |
@@ -947,7 +1279,7 @@ static int test(void) | |||
947 | err_cnt++; | 1279 | err_cnt++; |
948 | goto fail; | 1280 | goto fail; |
949 | } | 1281 | } |
950 | if (strstr(bpf_log_buf, tests[i].errstr) == 0) { | 1282 | if (strstr(bpf_log_buf, expected_errstr) == 0) { |
951 | printf("FAIL\nunexpected error message: %s", | 1283 | printf("FAIL\nunexpected error message: %s", |
952 | bpf_log_buf); | 1284 | bpf_log_buf); |
953 | err_cnt++; | 1285 | err_cnt++; |
@@ -960,6 +1292,8 @@ static int test(void) | |||
960 | fail: | 1292 | fail: |
961 | if (map_fd >= 0) | 1293 | if (map_fd >= 0) |
962 | close(map_fd); | 1294 | close(map_fd); |
1295 | if (prog_array_fd >= 0) | ||
1296 | close(prog_array_fd); | ||
963 | close(prog_fd); | 1297 | close(prog_fd); |
964 | 1298 | ||
965 | } | 1299 | } |
@@ -970,5 +1304,8 @@ fail: | |||
970 | 1304 | ||
971 | int main(void) | 1305 | int main(void) |
972 | { | 1306 | { |
1307 | struct rlimit r = {1 << 20, 1 << 20}; | ||
1308 | |||
1309 | setrlimit(RLIMIT_MEMLOCK, &r); | ||
973 | return test(); | 1310 | return test(); |
974 | } | 1311 | } |