bpf: add tail call tests to test_verifier
authorDaniel Borkmann <daniel@iogearbox.net>
Mon, 26 Feb 2018 21:34:33 +0000 (22:34 +0100)
committerAlexei Starovoitov <ast@kernel.org>
Tue, 27 Feb 2018 04:11:23 +0000 (20:11 -0800)
One of the downsides of the test_bpf module was that since being
in kernel space, it couldn't test-run tail calls. Now that the
test_verifier has the ability to perform run-time tests, populate
the prog array so we actually jump into other BPF programs and
can check all corner cases. Most useful in combination with JITs.

Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
tools/testing/selftests/bpf/test_verifier.c

index bd3a08c7cc15e25511add707058c91dcdc499b00..9eb05f3135ac92b964a4e512b4e3a7d58e00840f 100644 (file)
@@ -2588,6 +2588,62 @@ static struct bpf_test tests[] = {
                .result_unpriv = REJECT,
                .result = ACCEPT,
        },
+       {
+               "runtime/jit: tail_call within bounds, prog once",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_3, 0),
+                       BPF_LD_MAP_FD(BPF_REG_2, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_tail_call),
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup_prog = { 1 },
+               .result = ACCEPT,
+               .retval = 42,
+       },
+       {
+               "runtime/jit: tail_call within bounds, prog loop",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_3, 1),
+                       BPF_LD_MAP_FD(BPF_REG_2, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_tail_call),
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup_prog = { 1 },
+               .result = ACCEPT,
+               .retval = 41,
+       },
+       {
+               "runtime/jit: tail_call within bounds, no prog",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_3, 2),
+                       BPF_LD_MAP_FD(BPF_REG_2, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_tail_call),
+                       BPF_MOV64_IMM(BPF_REG_0, 1),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup_prog = { 1 },
+               .result = ACCEPT,
+               .retval = 1,
+       },
+       {
+               "runtime/jit: tail_call out of bounds",
+               .insns = {
+                       BPF_MOV64_IMM(BPF_REG_3, 256),
+                       BPF_LD_MAP_FD(BPF_REG_2, 0),
+                       BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                                    BPF_FUNC_tail_call),
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
+                       BPF_EXIT_INSN(),
+               },
+               .fixup_prog = { 1 },
+               .result = ACCEPT,
+               .retval = 2,
+       },
        {
                "runtime/jit: pass negative index to tail_call",
                .insns = {
@@ -2595,11 +2651,12 @@ static struct bpf_test tests[] = {
                        BPF_LD_MAP_FD(BPF_REG_2, 0),
                        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
                                     BPF_FUNC_tail_call),
-                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
                        BPF_EXIT_INSN(),
                },
                .fixup_prog = { 1 },
                .result = ACCEPT,
+               .retval = 2,
        },
        {
                "runtime/jit: pass > 32bit index to tail_call",
@@ -2608,11 +2665,12 @@ static struct bpf_test tests[] = {
                        BPF_LD_MAP_FD(BPF_REG_2, 0),
                        BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
                                     BPF_FUNC_tail_call),
-                       BPF_MOV64_IMM(BPF_REG_0, 0),
+                       BPF_MOV64_IMM(BPF_REG_0, 2),
                        BPF_EXIT_INSN(),
                },
                .fixup_prog = { 2 },
                .result = ACCEPT,
+               .retval = 42,
        },
        {
                "stack pointer arithmetic",
@@ -11278,16 +11336,61 @@ static int create_map(uint32_t size_value, uint32_t max_elem)
        return fd;
 }
 
+static int create_prog_dummy1(void)
+{
+       struct bpf_insn prog[] = {
+               BPF_MOV64_IMM(BPF_REG_0, 42),
+               BPF_EXIT_INSN(),
+       };
+
+       return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog,
+                               ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
+}
+
+static int create_prog_dummy2(int mfd, int idx)
+{
+       struct bpf_insn prog[] = {
+               BPF_MOV64_IMM(BPF_REG_3, idx),
+               BPF_LD_MAP_FD(BPF_REG_2, mfd),
+               BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
+                            BPF_FUNC_tail_call),
+               BPF_MOV64_IMM(BPF_REG_0, 41),
+               BPF_EXIT_INSN(),
+       };
+
+       return bpf_load_program(BPF_PROG_TYPE_SOCKET_FILTER, prog,
+                               ARRAY_SIZE(prog), "GPL", 0, NULL, 0);
+}
+
 static int create_prog_array(void)
 {
-       int fd;
+       int p1key = 0, p2key = 1;
+       int mfd, p1fd, p2fd;
 
-       fd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int),
-                           sizeof(int), 4, 0);
-       if (fd < 0)
+       mfd = bpf_create_map(BPF_MAP_TYPE_PROG_ARRAY, sizeof(int),
+                            sizeof(int), 4, 0);
+       if (mfd < 0) {
                printf("Failed to create prog array '%s'!\n", strerror(errno));
+               return -1;
+       }
 
-       return fd;
+       p1fd = create_prog_dummy1();
+       p2fd = create_prog_dummy2(mfd, p2key);
+       if (p1fd < 0 || p2fd < 0)
+               goto out;
+       if (bpf_map_update_elem(mfd, &p1key, &p1fd, BPF_ANY) < 0)
+               goto out;
+       if (bpf_map_update_elem(mfd, &p2key, &p2fd, BPF_ANY) < 0)
+               goto out;
+       close(p2fd);
+       close(p1fd);
+
+       return mfd;
+out:
+       close(p2fd);
+       close(p1fd);
+       close(mfd);
+       return -1;
 }
 
 static int create_map_in_map(void)