bpf: Add tests for map-in-map
Test cases for array of maps and hash of maps. Signed-off-by: Martin KaFai Lau <kafai@fb.com> Acked-by: Alexei Starovoitov <ast@kernel.org> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:

committed by
David S. Miller

parent
bcc6b1b7eb
commit
fb30d4b712
@@ -38,6 +38,7 @@
|
||||
|
||||
#define MAX_INSNS 512
|
||||
#define MAX_FIXUPS 8
|
||||
#define MAX_NR_MAPS 4
|
||||
|
||||
struct bpf_test {
|
||||
const char *descr;
|
||||
@@ -45,6 +46,7 @@ struct bpf_test {
|
||||
int fixup_map1[MAX_FIXUPS];
|
||||
int fixup_map2[MAX_FIXUPS];
|
||||
int fixup_prog[MAX_FIXUPS];
|
||||
int fixup_map_in_map[MAX_FIXUPS];
|
||||
const char *errstr;
|
||||
const char *errstr_unpriv;
|
||||
enum {
|
||||
@@ -4452,7 +4454,76 @@ static struct bpf_test tests[] = {
|
||||
.errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.",
|
||||
.result = REJECT,
|
||||
.result_unpriv = REJECT,
|
||||
}
|
||||
},
|
||||
{
|
||||
"map in map access",
|
||||
.insns = {
|
||||
BPF_ST_MEM(0, BPF_REG_10, -4, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
|
||||
BPF_ST_MEM(0, BPF_REG_10, -4, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_MOV64_REG(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map_in_map = { 3 },
|
||||
.result = ACCEPT,
|
||||
},
|
||||
{
|
||||
"invalid inner map pointer",
|
||||
.insns = {
|
||||
BPF_ST_MEM(0, BPF_REG_10, -4, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
|
||||
BPF_ST_MEM(0, BPF_REG_10, -4, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_MOV64_REG(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map_in_map = { 3 },
|
||||
.errstr = "R1 type=inv expected=map_ptr",
|
||||
.errstr_unpriv = "R1 pointer arithmetic prohibited",
|
||||
.result = REJECT,
|
||||
},
|
||||
{
|
||||
"forgot null checking on the inner map pointer",
|
||||
.insns = {
|
||||
BPF_ST_MEM(0, BPF_REG_10, -4, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_LD_MAP_FD(BPF_REG_1, 0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_ST_MEM(0, BPF_REG_10, -4, 0),
|
||||
BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
|
||||
BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
|
||||
BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
|
||||
BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
|
||||
BPF_FUNC_map_lookup_elem),
|
||||
BPF_MOV64_REG(BPF_REG_0, 0),
|
||||
BPF_EXIT_INSN(),
|
||||
},
|
||||
.fixup_map_in_map = { 3 },
|
||||
.errstr = "R1 type=map_value_or_null expected=map_ptr",
|
||||
.result = REJECT,
|
||||
},
|
||||
};
|
||||
|
||||
static int probe_filter_length(const struct bpf_insn *fp)
|
||||
@@ -4489,42 +4560,73 @@ static int create_prog_array(void)
|
||||
return fd;
|
||||
}
|
||||
|
||||
static int create_map_in_map(void)
|
||||
{
|
||||
int inner_map_fd, outer_map_fd;
|
||||
|
||||
inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int),
|
||||
sizeof(int), 1, 0);
|
||||
if (inner_map_fd < 0) {
|
||||
printf("Failed to create array '%s'!\n", strerror(errno));
|
||||
return inner_map_fd;
|
||||
}
|
||||
|
||||
outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS,
|
||||
sizeof(int), inner_map_fd, 1, 0);
|
||||
if (outer_map_fd < 0)
|
||||
printf("Failed to create array of maps '%s'!\n",
|
||||
strerror(errno));
|
||||
|
||||
close(inner_map_fd);
|
||||
|
||||
return outer_map_fd;
|
||||
}
|
||||
|
||||
static char bpf_vlog[32768];
|
||||
|
||||
static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog,
|
||||
int *fd_f1, int *fd_f2, int *fd_f3)
|
||||
int *map_fds)
|
||||
{
|
||||
int *fixup_map1 = test->fixup_map1;
|
||||
int *fixup_map2 = test->fixup_map2;
|
||||
int *fixup_prog = test->fixup_prog;
|
||||
int *fixup_map_in_map = test->fixup_map_in_map;
|
||||
|
||||
/* Allocating HTs with 1 elem is fine here, since we only test
|
||||
* for verifier and not do a runtime lookup, so the only thing
|
||||
* that really matters is value size in this case.
|
||||
*/
|
||||
if (*fixup_map1) {
|
||||
*fd_f1 = create_map(sizeof(long long), 1);
|
||||
map_fds[0] = create_map(sizeof(long long), 1);
|
||||
do {
|
||||
prog[*fixup_map1].imm = *fd_f1;
|
||||
prog[*fixup_map1].imm = map_fds[0];
|
||||
fixup_map1++;
|
||||
} while (*fixup_map1);
|
||||
}
|
||||
|
||||
if (*fixup_map2) {
|
||||
*fd_f2 = create_map(sizeof(struct test_val), 1);
|
||||
map_fds[1] = create_map(sizeof(struct test_val), 1);
|
||||
do {
|
||||
prog[*fixup_map2].imm = *fd_f2;
|
||||
prog[*fixup_map2].imm = map_fds[1];
|
||||
fixup_map2++;
|
||||
} while (*fixup_map2);
|
||||
}
|
||||
|
||||
if (*fixup_prog) {
|
||||
*fd_f3 = create_prog_array();
|
||||
map_fds[2] = create_prog_array();
|
||||
do {
|
||||
prog[*fixup_prog].imm = *fd_f3;
|
||||
prog[*fixup_prog].imm = map_fds[2];
|
||||
fixup_prog++;
|
||||
} while (*fixup_prog);
|
||||
}
|
||||
|
||||
if (*fixup_map_in_map) {
|
||||
map_fds[3] = create_map_in_map();
|
||||
do {
|
||||
prog[*fixup_map_in_map].imm = map_fds[3];
|
||||
fixup_map_in_map++;
|
||||
} while (*fixup_map_in_map);
|
||||
}
|
||||
}
|
||||
|
||||
static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||
@@ -4533,11 +4635,15 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||
struct bpf_insn *prog = test->insns;
|
||||
int prog_len = probe_filter_length(prog);
|
||||
int prog_type = test->prog_type;
|
||||
int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1;
|
||||
int map_fds[MAX_NR_MAPS];
|
||||
int fd_prog, expected_ret;
|
||||
const char *expected_err;
|
||||
int i;
|
||||
|
||||
do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3);
|
||||
for (i = 0; i < MAX_NR_MAPS; i++)
|
||||
map_fds[i] = -1;
|
||||
|
||||
do_test_fixup(test, prog, map_fds);
|
||||
|
||||
fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
|
||||
prog, prog_len, "GPL", 0, bpf_vlog,
|
||||
@@ -4568,9 +4674,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
|
||||
printf("OK\n");
|
||||
close_fds:
|
||||
close(fd_prog);
|
||||
close(fd_f1);
|
||||
close(fd_f2);
|
||||
close(fd_f3);
|
||||
for (i = 0; i < MAX_NR_MAPS; i++)
|
||||
close(map_fds[i]);
|
||||
sched_yield();
|
||||
return;
|
||||
fail_log:
|
||||
|
Reference in New Issue
Block a user