Merge git://git.kernel.org/pub/scm/linux/kernel/git/bpf/bpf-next
[linux-2.6-microblaze.git] / tools / testing / selftests / bpf / test_verifier.c
index c3b799c..baafe5c 100644 (file)
@@ -49,6 +49,7 @@
 #define MAX_INSNS      BPF_MAXINSNS
 #define MAX_FIXUPS     8
 #define MAX_NR_MAPS    13
+#define MAX_TEST_RUNS  8
 #define POINTER_VALUE  0xcafe4all
 #define TEST_DATA_LEN  64
 
@@ -76,7 +77,7 @@ struct bpf_test {
        int fixup_percpu_cgroup_storage[MAX_FIXUPS];
        const char *errstr;
        const char *errstr_unpriv;
-       uint32_t retval, retval_unpriv;
+       uint32_t retval, retval_unpriv, insn_processed;
        enum {
                UNDEF,
                ACCEPT,
@@ -86,6 +87,14 @@ struct bpf_test {
        uint8_t flags;
        __u8 data[TEST_DATA_LEN];
        void (*fill_helper)(struct bpf_test *self);
+       uint8_t runs;
+       struct {
+               uint32_t retval, retval_unpriv;
+               union {
+                       __u8 data[TEST_DATA_LEN];
+                       __u64 data64[TEST_DATA_LEN / 8];
+               };
+       } retvals[MAX_TEST_RUNS];
 };
 
 /* Note we want this to be 64 bit aligned so that the end of our array is
@@ -1001,14 +1010,44 @@ static struct bpf_test tests[] = {
                        BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
                        /* mess up with R1 pointer on stack */
                        BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23),
-                       /* fill back into R0 should fail */
+                       /* fill back into R0 is fine for priv.
+                        * R0 now becomes SCALAR_VALUE.
+                        */
                        BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+                       /* Load from R0 should fail. */
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_0, 8),
                        BPF_EXIT_INSN(),
                },
                .errstr_unpriv = "attempt to corrupt spilled",
-               .errstr = "corrupted spill",
+               .errstr = "R0 invalid mem access 'inv",
                .result = REJECT,
        },
+       {
+               "check corrupted spill/fill, LSB",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
+                       BPF_ST_MEM(BPF_H, BPF_REG_10, -8, 0xcafe),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "attempt to corrupt spilled",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+               .retval = POINTER_VALUE,
+       },
+       {
+               "check corrupted spill/fill, MSB",
+               .insns = {
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
+                       BPF_ST_MEM(BPF_W, BPF_REG_10, -4, 0x12345678),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
+                       BPF_EXIT_INSN(),
+               },
+               .errstr_unpriv = "attempt to corrupt spilled",
+               .result_unpriv = REJECT,
+               .result = ACCEPT,
+               .retval = POINTER_VALUE,
+       },
        {
                "invalid src register in STX",
                .insns = {
@@ -1813,10 +1852,20 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SK_SKB,
        },
        {
-               "invalid 64B read of family in SK_MSG",
+               "valid access size in SK_MSG",
+               .insns = {
+                       BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
+                                   offsetof(struct sk_msg_md, size)),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .prog_type = BPF_PROG_TYPE_SK_MSG,
+       },
+       {
+               "invalid 64B read of size in SK_MSG",
                .insns = {
                        BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1,
-                                   offsetof(struct sk_msg_md, family)),
+                                   offsetof(struct sk_msg_md, size)),
                        BPF_EXIT_INSN(),
                },
                .errstr = "invalid bpf_context access",
@@ -1827,10 +1876,10 @@ static struct bpf_test tests[] = {
                "invalid read past end of SK_MSG",
                .insns = {
                        BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
-                                   offsetof(struct sk_msg_md, local_port) + 4),
+                                   offsetof(struct sk_msg_md, size) + 4),
                        BPF_EXIT_INSN(),
                },
-               .errstr = "R0 !read_ok",
+               .errstr = "invalid bpf_context access",
                .result = REJECT,
                .prog_type = BPF_PROG_TYPE_SK_MSG,
        },
@@ -13647,6 +13696,28 @@ static struct bpf_test tests[] = {
                .prog_type = BPF_PROG_TYPE_SCHED_CLS,
                .result = ACCEPT,
        },
+       {
+               "allocated_stack",
+               .insns = {
+                       BPF_ALU64_REG(BPF_MOV, BPF_REG_6, BPF_REG_1),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_get_prandom_u32),
+                       BPF_ALU64_REG(BPF_MOV, BPF_REG_7, BPF_REG_0),
+                       BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_6, -8),
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_6, BPF_REG_10, -8),
+                       BPF_STX_MEM(BPF_B, BPF_REG_10, BPF_REG_7, -9),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_7, BPF_REG_10, -9),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .result = ACCEPT,
+               .result_unpriv = ACCEPT,
+               .insn_processed = 15,
+       },
        {
                "reference tracking in call: free reference in subprog and outside",
                .insns = {
@@ -14099,6 +14170,7 @@ static struct bpf_test tests[] = {
                .errstr_unpriv = "R1 leaks addr",
                .result = REJECT,
        },
+       {
                "calls: cross frame pruning",
                .insns = {
                        /* r8 = !!random();
@@ -14122,10 +14194,199 @@ static struct bpf_test tests[] = {
                },
                .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
                .errstr_unpriv = "function calls to other bpf functions are allowed for root only",
+                .result = REJECT,
+       },
+       {
+               "jset: functional",
+               .insns = {
+                       /* r0 = 0 */
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       /* prep for direct packet access via r2 */
+                       BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8),
+                       BPF_JMP_REG(BPF_JLE, BPF_REG_4, BPF_REG_3, 1),
+                       BPF_EXIT_INSN(),
+
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_2, 0),
+
+                       /* reg, bit 63 or bit 0 set, taken */
+                       BPF_LD_IMM64(BPF_REG_8, 0x8000000000000001),
+                       BPF_JMP_REG(BPF_JSET, BPF_REG_7, BPF_REG_8, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* reg, bit 62, not taken */
+                       BPF_LD_IMM64(BPF_REG_8, 0x4000000000000000),
+                       BPF_JMP_REG(BPF_JSET, BPF_REG_7, BPF_REG_8, 1),
+                       BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* imm, any bit set, taken */
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_7, -1, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* imm, bit 31 set, taken */
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x80000000, 1),
+                       BPF_EXIT_INSN(),
+
+                       /* all good - return r0 == 2 */
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .runs = 7,
+               .retvals = {
+                       { .retval = 2,
+                         .data64 = { (1ULL << 63) | (1U << 31) | (1U << 0), }
+                       },
+                       { .retval = 2,
+                         .data64 = { (1ULL << 63) | (1U << 31), }
+                       },
+                       { .retval = 2,
+                         .data64 = { (1ULL << 31) | (1U << 0), }
+                       },
+                       { .retval = 2,
+                         .data64 = { (__u32)-1, }
+                       },
+                       { .retval = 2,
+                         .data64 = { ~0x4000000000000000ULL, }
+                       },
+                       { .retval = 0,
+                         .data64 = { 0, }
+                       },
+                       { .retval = 0,
+                         .data64 = { ~0ULL, }
+                       },
+               },
+       },
+       {
+               "jset: sign-extend",
+               .insns = {
+                       /* r0 = 0 */
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       /* prep for direct packet access via r2 */
+                       BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data)),
+                       BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1,
+                                   offsetof(struct __sk_buff, data_end)),
+                       BPF_MOV64_REG(BPF_REG_4, BPF_REG_2),
+                       BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 8),
+                       BPF_JMP_REG(BPF_JLE, BPF_REG_4, BPF_REG_3, 1),
+                       BPF_EXIT_INSN(),
+
+                       BPF_LDX_MEM(BPF_DW, BPF_REG_7, BPF_REG_2, 0),
+
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_7, 0x80000000, 1),
+                       BPF_EXIT_INSN(),
+
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SCHED_CLS,
+               .result = ACCEPT,
+               .retval = 2,
+               .data = { 1, 0, 0, 0, 0, 0, 0, 1, },
+       },
+       {
+               "jset: known const compare",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .retval_unpriv = 1,
+               .result_unpriv = ACCEPT,
+               .retval = 1,
+               .result = ACCEPT,
+       },
+       {
+               "jset: known const compare bad",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "!read_ok",
                .result_unpriv = REJECT,
                .errstr = "!read_ok",
                .result = REJECT,
        },
+       {
+               "jset: unknown const compare taken",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_JMP_IMM(BPF_JA, 0, 0, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "!read_ok",
+               .result_unpriv = REJECT,
+               .errstr = "!read_ok",
+               .result = REJECT,
+       },
+       {
+               "jset: unknown const compare not taken",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 1, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .errstr_unpriv = "!read_ok",
+               .result_unpriv = REJECT,
+               .errstr = "!read_ok",
+               .result = REJECT,
+       },
+       {
+               "jset: half-known const compare",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_ALU64_IMM(BPF_OR, BPF_REG_0, 2),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_0, 3, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .result_unpriv = ACCEPT,
+               .result = ACCEPT,
+       },
+       {
+               "jset: range",
+               .insns = {
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_get_prandom_u32),
+                       BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
+                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_ALU64_IMM(BPF_AND, BPF_REG_1, 0xff),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_1, 0xf0, 3),
+                       BPF_JMP_IMM(BPF_JLT, BPF_REG_1, 0x10, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+                       BPF_JMP_IMM(BPF_JSET, BPF_REG_1, 0x10, 1),
+                       BPF_EXIT_INSN(),
+                       BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0x10, 1),
+                       BPF_LDX_MEM(BPF_B, BPF_REG_8, BPF_REG_9, 0),
+                       BPF_EXIT_INSN(),
+               },
+               .prog_type = BPF_PROG_TYPE_SOCKET_FILTER,
+               .result_unpriv = ACCEPT,
+               .result = ACCEPT,
+       },
 };
 
 static int probe_filter_length(const struct bpf_insn *fp)
@@ -14408,16 +14669,42 @@ out:
        return ret;
 }
 
+static int do_prog_test_run(int fd_prog, bool unpriv, uint32_t expected_val,
+                           void *data, size_t size_data)
+{
+       __u8 tmp[TEST_DATA_LEN << 2];
+       __u32 size_tmp = sizeof(tmp);
+       uint32_t retval;
+       int err;
+
+       if (unpriv)
+               set_admin(true);
+       err = bpf_prog_test_run(fd_prog, 1, data, size_data,
+                               tmp, &size_tmp, &retval, NULL);
+       if (unpriv)
+               set_admin(false);
+       if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
+               printf("Unexpected bpf_prog_test_run error ");
+               return err;
+       }
+       if (!err && retval != expected_val &&
+           expected_val != POINTER_VALUE) {
+               printf("FAIL retval %d != %d ", retval, expected_val);
+               return 1;
+       }
+
+       return 0;
+}
+
 static void do_test_single(struct bpf_test *test, bool unpriv,
                           int *passes, int *errors)
 {
        int fd_prog, expected_ret, alignment_prevented_execution;
        int prog_len, prog_type = test->prog_type;
        struct bpf_insn *prog = test->insns;
+       int run_errs, run_successes;
        int map_fds[MAX_NR_MAPS];
        const char *expected_err;
-       uint32_t expected_val;
-       uint32_t retval;
        __u32 pflags;
        int i, err;
 
@@ -14441,8 +14728,6 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
                       test->result_unpriv : test->result;
        expected_err = unpriv && test->errstr_unpriv ?
                       test->errstr_unpriv : test->errstr;
-       expected_val = unpriv && test->retval_unpriv ?
-                      test->retval_unpriv : test->retval;
 
        alignment_prevented_execution = 0;
 
@@ -14454,10 +14739,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
                }
 #ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
                if (fd_prog >= 0 &&
-                   (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS)) {
+                   (test->flags & F_NEEDS_EFFICIENT_UNALIGNED_ACCESS))
                        alignment_prevented_execution = 1;
-                       goto test_ok;
-               }
 #endif
        } else {
                if (fd_prog >= 0) {
@@ -14471,33 +14754,67 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
                }
        }
 
-       if (fd_prog >= 0) {
-               __u8 tmp[TEST_DATA_LEN << 2];
-               __u32 size_tmp = sizeof(tmp);
-
-               if (unpriv)
-                       set_admin(true);
-               err = bpf_prog_test_run(fd_prog, 1, test->data,
-                                       sizeof(test->data), tmp, &size_tmp,
-                                       &retval, NULL);
-               if (unpriv)
-                       set_admin(false);
-               if (err && errno != 524/*ENOTSUPP*/ && errno != EPERM) {
-                       printf("Unexpected bpf_prog_test_run error\n");
+       if (test->insn_processed) {
+               uint32_t insn_processed;
+               char *proc;
+
+               proc = strstr(bpf_vlog, "processed ");
+               insn_processed = atoi(proc + 10);
+               if (test->insn_processed != insn_processed) {
+                       printf("FAIL\nUnexpected insn_processed %u vs %u\n",
+                              insn_processed, test->insn_processed);
                        goto fail_log;
                }
-               if (!err && retval != expected_val &&
-                   expected_val != POINTER_VALUE) {
-                       printf("FAIL retval %d != %d\n", retval, expected_val);
-                       goto fail_log;
+       }
+
+       run_errs = 0;
+       run_successes = 0;
+       if (!alignment_prevented_execution && fd_prog >= 0) {
+               uint32_t expected_val;
+               int i;
+
+               if (!test->runs) {
+                       expected_val = unpriv && test->retval_unpriv ?
+                               test->retval_unpriv : test->retval;
+
+                       err = do_prog_test_run(fd_prog, unpriv, expected_val,
+                                              test->data, sizeof(test->data));
+                       if (err)
+                               run_errs++;
+                       else
+                               run_successes++;
+               }
+
+               for (i = 0; i < test->runs; i++) {
+                       if (unpriv && test->retvals[i].retval_unpriv)
+                               expected_val = test->retvals[i].retval_unpriv;
+                       else
+                               expected_val = test->retvals[i].retval;
+
+                       err = do_prog_test_run(fd_prog, unpriv, expected_val,
+                                              test->retvals[i].data,
+                                              sizeof(test->retvals[i].data));
+                       if (err) {
+                               printf("(run %d/%d) ", i + 1, test->runs);
+                               run_errs++;
+                       } else {
+                               run_successes++;
+                       }
                }
        }
-#ifndef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
-test_ok:
-#endif
-       (*passes)++;
-       printf("OK%s\n", alignment_prevented_execution ?
-              " (NOTE: not executed due to unknown alignment)" : "");
+
+       if (!run_errs) {
+               (*passes)++;
+               if (run_successes > 1)
+                       printf("%d cases ", run_successes);
+               printf("OK");
+               if (alignment_prevented_execution)
+                       printf(" (NOTE: not executed due to unknown alignment)");
+               printf("\n");
+       } else {
+               printf("\n");
+               goto fail_log;
+       }
 close_fds:
        close(fd_prog);
        for (i = 0; i < MAX_NR_MAPS; i++)