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 | |
| 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')
| -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 | } |
