summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMartin KaFai Lau <kafai@fb.com>2017-03-22 13:00:35 -0400
committerDavid S. Miller <davem@davemloft.net>2017-03-22 18:45:45 -0400
commitfb30d4b71214aa1811e997f8f753b14b46d5b912 (patch)
treea546eefbd355792aa544c0f220b38bab4493b1aa
parentbcc6b1b7ebf857a9fe56202e2be3361131588c15 (diff)
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>
-rw-r--r--samples/bpf/Makefile4
-rw-r--r--samples/bpf/bpf_helpers.h1
-rw-r--r--samples/bpf/bpf_load.c22
-rw-r--r--samples/bpf/test_map_in_map_kern.c173
-rw-r--r--samples/bpf/test_map_in_map_user.c116
-rw-r--r--tools/include/uapi/linux/bpf.h3
-rw-r--r--tools/lib/bpf/bpf.c17
-rw-r--r--tools/lib/bpf/bpf.h2
-rw-r--r--tools/testing/selftests/bpf/test_verifier.c131
9 files changed, 451 insertions, 18 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
index 09e9d535bd74..91c1d616d975 100644
--- a/samples/bpf/Makefile
+++ b/samples/bpf/Makefile
@@ -34,6 +34,7 @@ hostprogs-y += sampleip
34hostprogs-y += tc_l2_redirect 34hostprogs-y += tc_l2_redirect
35hostprogs-y += lwt_len_hist 35hostprogs-y += lwt_len_hist
36hostprogs-y += xdp_tx_iptunnel 36hostprogs-y += xdp_tx_iptunnel
37hostprogs-y += test_map_in_map
37 38
38# Libbpf dependencies 39# Libbpf dependencies
39LIBBPF := ../../tools/lib/bpf/bpf.o 40LIBBPF := ../../tools/lib/bpf/bpf.o
@@ -72,6 +73,7 @@ sampleip-objs := bpf_load.o $(LIBBPF) sampleip_user.o
72tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o 73tc_l2_redirect-objs := bpf_load.o $(LIBBPF) tc_l2_redirect_user.o
73lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o 74lwt_len_hist-objs := bpf_load.o $(LIBBPF) lwt_len_hist_user.o
74xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o 75xdp_tx_iptunnel-objs := bpf_load.o $(LIBBPF) xdp_tx_iptunnel_user.o
76test_map_in_map-objs := bpf_load.o $(LIBBPF) test_map_in_map_user.o
75 77
76# Tell kbuild to always build the programs 78# Tell kbuild to always build the programs
77always := $(hostprogs-y) 79always := $(hostprogs-y)
@@ -105,6 +107,7 @@ always += trace_event_kern.o
105always += sampleip_kern.o 107always += sampleip_kern.o
106always += lwt_len_hist_kern.o 108always += lwt_len_hist_kern.o
107always += xdp_tx_iptunnel_kern.o 109always += xdp_tx_iptunnel_kern.o
110always += test_map_in_map_kern.o
108 111
109HOSTCFLAGS += -I$(objtree)/usr/include 112HOSTCFLAGS += -I$(objtree)/usr/include
110HOSTCFLAGS += -I$(srctree)/tools/lib/ 113HOSTCFLAGS += -I$(srctree)/tools/lib/
@@ -139,6 +142,7 @@ HOSTLOADLIBES_sampleip += -lelf
139HOSTLOADLIBES_tc_l2_redirect += -l elf 142HOSTLOADLIBES_tc_l2_redirect += -l elf
140HOSTLOADLIBES_lwt_len_hist += -l elf 143HOSTLOADLIBES_lwt_len_hist += -l elf
141HOSTLOADLIBES_xdp_tx_iptunnel += -lelf 144HOSTLOADLIBES_xdp_tx_iptunnel += -lelf
145HOSTLOADLIBES_test_map_in_map += -lelf
142 146
143# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline: 147# Allows pointing LLC/CLANG to a LLVM backend with bpf support, redefine on cmdline:
144# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang 148# make samples/bpf/ LLC=~/git/llvm/build/bin/llc CLANG=~/git/llvm/build/bin/clang
diff --git a/samples/bpf/bpf_helpers.h b/samples/bpf/bpf_helpers.h
index faaffe2e139a..52de9d88c021 100644
--- a/samples/bpf/bpf_helpers.h
+++ b/samples/bpf/bpf_helpers.h
@@ -80,6 +80,7 @@ struct bpf_map_def {
80 unsigned int value_size; 80 unsigned int value_size;
81 unsigned int max_entries; 81 unsigned int max_entries;
82 unsigned int map_flags; 82 unsigned int map_flags;
83 unsigned int inner_map_idx;
83}; 84};
84 85
85static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) = 86static int (*bpf_skb_load_bytes)(void *ctx, int off, void *to, int len) =
diff --git a/samples/bpf/bpf_load.c b/samples/bpf/bpf_load.c
index b86ee54da2d1..dcdce1270d38 100644
--- a/samples/bpf/bpf_load.c
+++ b/samples/bpf/bpf_load.c
@@ -43,6 +43,7 @@ struct bpf_map_def {
43 unsigned int value_size; 43 unsigned int value_size;
44 unsigned int max_entries; 44 unsigned int max_entries;
45 unsigned int map_flags; 45 unsigned int map_flags;
46 unsigned int inner_map_idx;
46}; 47};
47 48
48static int populate_prog_array(const char *event, int prog_fd) 49static int populate_prog_array(const char *event, int prog_fd)
@@ -198,11 +199,22 @@ static int load_maps(struct bpf_map_def *maps, int len)
198 199
199 for (i = 0; i < len / sizeof(struct bpf_map_def); i++) { 200 for (i = 0; i < len / sizeof(struct bpf_map_def); i++) {
200 201
201 map_fd[i] = bpf_create_map(maps[i].type, 202 if (maps[i].type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
202 maps[i].key_size, 203 maps[i].type == BPF_MAP_TYPE_HASH_OF_MAPS) {
203 maps[i].value_size, 204 int inner_map_fd = map_fd[maps[i].inner_map_idx];
204 maps[i].max_entries, 205
205 maps[i].map_flags); 206 map_fd[i] = bpf_create_map_in_map(maps[i].type,
207 maps[i].key_size,
208 inner_map_fd,
209 maps[i].max_entries,
210 maps[i].map_flags);
211 } else {
212 map_fd[i] = bpf_create_map(maps[i].type,
213 maps[i].key_size,
214 maps[i].value_size,
215 maps[i].max_entries,
216 maps[i].map_flags);
217 }
206 if (map_fd[i] < 0) { 218 if (map_fd[i] < 0) {
207 printf("failed to create a map: %d %s\n", 219 printf("failed to create a map: %d %s\n",
208 errno, strerror(errno)); 220 errno, strerror(errno));
diff --git a/samples/bpf/test_map_in_map_kern.c b/samples/bpf/test_map_in_map_kern.c
new file mode 100644
index 000000000000..42c44d091dd1
--- /dev/null
+++ b/samples/bpf/test_map_in_map_kern.c
@@ -0,0 +1,173 @@
1/*
2 * Copyright (c) 2017 Facebook
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 */
8#define KBUILD_MODNAME "foo"
9#include <linux/ptrace.h>
10#include <linux/version.h>
11#include <uapi/linux/bpf.h>
12#include <uapi/linux/in6.h>
13#include "bpf_helpers.h"
14
15#define MAX_NR_PORTS 65536
16
17/* map #0 */
18struct bpf_map_def SEC("maps") port_a = {
19 .type = BPF_MAP_TYPE_ARRAY,
20 .key_size = sizeof(u32),
21 .value_size = sizeof(int),
22 .max_entries = MAX_NR_PORTS,
23};
24
25/* map #1 */
26struct bpf_map_def SEC("maps") port_h = {
27 .type = BPF_MAP_TYPE_HASH,
28 .key_size = sizeof(u32),
29 .value_size = sizeof(int),
30 .max_entries = 1,
31};
32
33/* map #2 */
34struct bpf_map_def SEC("maps") reg_result_h = {
35 .type = BPF_MAP_TYPE_HASH,
36 .key_size = sizeof(u32),
37 .value_size = sizeof(int),
38 .max_entries = 1,
39};
40
41/* map #3 */
42struct bpf_map_def SEC("maps") inline_result_h = {
43 .type = BPF_MAP_TYPE_HASH,
44 .key_size = sizeof(u32),
45 .value_size = sizeof(int),
46 .max_entries = 1,
47};
48
49/* map #4 */ /* Test case #0 */
50struct bpf_map_def SEC("maps") a_of_port_a = {
51 .type = BPF_MAP_TYPE_ARRAY_OF_MAPS,
52 .key_size = sizeof(u32),
53 .inner_map_idx = 0, /* map_fd[0] is port_a */
54 .max_entries = MAX_NR_PORTS,
55};
56
57/* map #5 */ /* Test case #1 */
58struct bpf_map_def SEC("maps") h_of_port_a = {
59 .type = BPF_MAP_TYPE_HASH_OF_MAPS,
60 .key_size = sizeof(u32),
61 .inner_map_idx = 0, /* map_fd[0] is port_a */
62 .max_entries = 1,
63};
64
65/* map #6 */ /* Test case #2 */
66struct bpf_map_def SEC("maps") h_of_port_h = {
67 .type = BPF_MAP_TYPE_HASH_OF_MAPS,
68 .key_size = sizeof(u32),
69 .inner_map_idx = 1, /* map_fd[1] is port_h */
70 .max_entries = 1,
71};
72
73static __always_inline int do_reg_lookup(void *inner_map, u32 port)
74{
75 int *result;
76
77 result = bpf_map_lookup_elem(inner_map, &port);
78 return result ? *result : -ENOENT;
79}
80
81static __always_inline int do_inline_array_lookup(void *inner_map, u32 port)
82{
83 int *result;
84
85 if (inner_map != &port_a)
86 return -EINVAL;
87
88 result = bpf_map_lookup_elem(&port_a, &port);
89 return result ? *result : -ENOENT;
90}
91
92static __always_inline int do_inline_hash_lookup(void *inner_map, u32 port)
93{
94 int *result;
95
96 if (inner_map != &port_h)
97 return -EINVAL;
98
99 result = bpf_map_lookup_elem(&port_h, &port);
100 return result ? *result : -ENOENT;
101}
102
103SEC("kprobe/sys_connect")
104int trace_sys_connect(struct pt_regs *ctx)
105{
106 struct sockaddr_in6 *in6;
107 u16 test_case, port, dst6[8];
108 int addrlen, ret, inline_ret, ret_key = 0;
109 u32 port_key;
110 void *outer_map, *inner_map;
111 bool inline_hash = false;
112
113 in6 = (struct sockaddr_in6 *)PT_REGS_PARM2(ctx);
114 addrlen = (int)PT_REGS_PARM3(ctx);
115
116 if (addrlen != sizeof(*in6))
117 return 0;
118
119 ret = bpf_probe_read(dst6, sizeof(dst6), &in6->sin6_addr);
120 if (ret) {
121 inline_ret = ret;
122 goto done;
123 }
124
125 if (dst6[0] != 0xdead || dst6[1] != 0xbeef)
126 return 0;
127
128 test_case = dst6[7];
129
130 ret = bpf_probe_read(&port, sizeof(port), &in6->sin6_port);
131 if (ret) {
132 inline_ret = ret;
133 goto done;
134 }
135
136 port_key = port;
137
138 ret = -ENOENT;
139 if (test_case == 0) {
140 outer_map = &a_of_port_a;
141 } else if (test_case == 1) {
142 outer_map = &h_of_port_a;
143 } else if (test_case == 2) {
144 outer_map = &h_of_port_h;
145 } else {
146 ret = __LINE__;
147 inline_ret = ret;
148 goto done;
149 }
150
151 inner_map = bpf_map_lookup_elem(outer_map, &port_key);
152 if (!inner_map) {
153 ret = __LINE__;
154 inline_ret = ret;
155 goto done;
156 }
157
158 ret = do_reg_lookup(inner_map, port_key);
159
160 if (test_case == 0 || test_case == 1)
161 inline_ret = do_inline_array_lookup(inner_map, port_key);
162 else
163 inline_ret = do_inline_hash_lookup(inner_map, port_key);
164
165done:
166 bpf_map_update_elem(&reg_result_h, &ret_key, &ret, BPF_ANY);
167 bpf_map_update_elem(&inline_result_h, &ret_key, &inline_ret, BPF_ANY);
168
169 return 0;
170}
171
172char _license[] SEC("license") = "GPL";
173u32 _version SEC("version") = LINUX_VERSION_CODE;
diff --git a/samples/bpf/test_map_in_map_user.c b/samples/bpf/test_map_in_map_user.c
new file mode 100644
index 000000000000..f62fdc2bd428
--- /dev/null
+++ b/samples/bpf/test_map_in_map_user.c
@@ -0,0 +1,116 @@
1/*
2 * Copyright (c) 2017 Facebook
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of version 2 of the GNU General Public
6 * License as published by the Free Software Foundation.
7 */
8#include <sys/resource.h>
9#include <sys/socket.h>
10#include <arpa/inet.h>
11#include <stdint.h>
12#include <assert.h>
13#include <errno.h>
14#include <stdlib.h>
15#include <stdio.h>
16#include "libbpf.h"
17#include "bpf_load.h"
18
19#define PORT_A (map_fd[0])
20#define PORT_H (map_fd[1])
21#define REG_RESULT_H (map_fd[2])
22#define INLINE_RESULT_H (map_fd[3])
23#define A_OF_PORT_A (map_fd[4]) /* Test case #0 */
24#define H_OF_PORT_A (map_fd[5]) /* Test case #1 */
25#define H_OF_PORT_H (map_fd[6]) /* Test case #2 */
26
27static const char * const test_names[] = {
28 "Array of Array",
29 "Hash of Array",
30 "Hash of Hash",
31};
32
33#define NR_TESTS (sizeof(test_names) / sizeof(*test_names))
34
35static void populate_map(uint32_t port_key, int magic_result)
36{
37 int ret;
38
39 ret = bpf_map_update_elem(PORT_A, &port_key, &magic_result, BPF_ANY);
40 assert(!ret);
41
42 ret = bpf_map_update_elem(PORT_H, &port_key, &magic_result,
43 BPF_NOEXIST);
44 assert(!ret);
45
46 ret = bpf_map_update_elem(A_OF_PORT_A, &port_key, &PORT_A, BPF_ANY);
47 assert(!ret);
48
49 ret = bpf_map_update_elem(H_OF_PORT_A, &port_key, &PORT_A, BPF_NOEXIST);
50 assert(!ret);
51
52 ret = bpf_map_update_elem(H_OF_PORT_H, &port_key, &PORT_H, BPF_NOEXIST);
53 assert(!ret);
54}
55
56static void test_map_in_map(void)
57{
58 struct sockaddr_in6 in6 = { .sin6_family = AF_INET6 };
59 uint32_t result_key = 0, port_key;
60 int result, inline_result;
61 int magic_result = 0xfaceb00c;
62 int ret;
63 int i;
64
65 port_key = rand() & 0x00FF;
66 populate_map(port_key, magic_result);
67
68 in6.sin6_addr.s6_addr16[0] = 0xdead;
69 in6.sin6_addr.s6_addr16[1] = 0xbeef;
70 in6.sin6_port = port_key;
71
72 for (i = 0; i < NR_TESTS; i++) {
73 printf("%s: ", test_names[i]);
74
75 in6.sin6_addr.s6_addr16[7] = i;
76 ret = connect(-1, (struct sockaddr *)&in6, sizeof(in6));
77 assert(ret == -1 && errno == EBADF);
78
79 ret = bpf_map_lookup_elem(REG_RESULT_H, &result_key, &result);
80 assert(!ret);
81
82 ret = bpf_map_lookup_elem(INLINE_RESULT_H, &result_key,
83 &inline_result);
84 assert(!ret);
85
86 if (result != magic_result || inline_result != magic_result) {
87 printf("Error. result:%d inline_result:%d\n",
88 result, inline_result);
89 exit(1);
90 }
91
92 bpf_map_delete_elem(REG_RESULT_H, &result_key);
93 bpf_map_delete_elem(INLINE_RESULT_H, &result_key);
94
95 printf("Pass\n");
96 }
97}
98
99int main(int argc, char **argv)
100{
101 struct rlimit r = {RLIM_INFINITY, RLIM_INFINITY};
102 char filename[256];
103
104 assert(!setrlimit(RLIMIT_MEMLOCK, &r));
105
106 snprintf(filename, sizeof(filename), "%s_kern.o", argv[0]);
107
108 if (load_bpf_file(filename)) {
109 printf("%s", bpf_log_buf);
110 return 1;
111 }
112
113 test_map_in_map();
114
115 return 0;
116}
diff --git a/tools/include/uapi/linux/bpf.h b/tools/include/uapi/linux/bpf.h
index 0539a0ceef38..ce6f029ac368 100644
--- a/tools/include/uapi/linux/bpf.h
+++ b/tools/include/uapi/linux/bpf.h
@@ -96,6 +96,8 @@ enum bpf_map_type {
96 BPF_MAP_TYPE_LRU_HASH, 96 BPF_MAP_TYPE_LRU_HASH,
97 BPF_MAP_TYPE_LRU_PERCPU_HASH, 97 BPF_MAP_TYPE_LRU_PERCPU_HASH,
98 BPF_MAP_TYPE_LPM_TRIE, 98 BPF_MAP_TYPE_LPM_TRIE,
99 BPF_MAP_TYPE_ARRAY_OF_MAPS,
100 BPF_MAP_TYPE_HASH_OF_MAPS,
99}; 101};
100 102
101enum bpf_prog_type { 103enum bpf_prog_type {
@@ -152,6 +154,7 @@ union bpf_attr {
152 __u32 value_size; /* size of value in bytes */ 154 __u32 value_size; /* size of value in bytes */
153 __u32 max_entries; /* max number of entries in a map */ 155 __u32 max_entries; /* max number of entries in a map */
154 __u32 map_flags; /* prealloc or not */ 156 __u32 map_flags; /* prealloc or not */
157 __u32 inner_map_fd; /* fd pointing to the inner map */
155 }; 158 };
156 159
157 struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */ 160 struct { /* anonymous struct used by BPF_MAP_*_ELEM commands */
diff --git a/tools/lib/bpf/bpf.c b/tools/lib/bpf/bpf.c
index 207c2eeddab0..9b58d20e8c93 100644
--- a/tools/lib/bpf/bpf.c
+++ b/tools/lib/bpf/bpf.c
@@ -69,6 +69,23 @@ int bpf_create_map(enum bpf_map_type map_type, int key_size,
69 return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr)); 69 return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
70} 70}
71 71
72int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
73 int inner_map_fd, int max_entries, __u32 map_flags)
74{
75 union bpf_attr attr;
76
77 memset(&attr, '\0', sizeof(attr));
78
79 attr.map_type = map_type;
80 attr.key_size = key_size;
81 attr.value_size = 4;
82 attr.inner_map_fd = inner_map_fd;
83 attr.max_entries = max_entries;
84 attr.map_flags = map_flags;
85
86 return sys_bpf(BPF_MAP_CREATE, &attr, sizeof(attr));
87}
88
72int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns, 89int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
73 size_t insns_cnt, const char *license, 90 size_t insns_cnt, const char *license,
74 __u32 kern_version, char *log_buf, size_t log_buf_sz) 91 __u32 kern_version, char *log_buf, size_t log_buf_sz)
diff --git a/tools/lib/bpf/bpf.h b/tools/lib/bpf/bpf.h
index 09c3dcac0496..93f021932623 100644
--- a/tools/lib/bpf/bpf.h
+++ b/tools/lib/bpf/bpf.h
@@ -26,6 +26,8 @@
26 26
27int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size, 27int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
28 int max_entries, __u32 map_flags); 28 int max_entries, __u32 map_flags);
29int bpf_create_map_in_map(enum bpf_map_type map_type, int key_size,
30 int inner_map_fd, int max_entries, __u32 map_flags);
29 31
30/* Recommend log buffer size */ 32/* Recommend log buffer size */
31#define BPF_LOG_BUF_SIZE 65536 33#define BPF_LOG_BUF_SIZE 65536
diff --git a/tools/testing/selftests/bpf/test_verifier.c b/tools/testing/selftests/bpf/test_verifier.c
index d1555e4240c0..f4f43c98cf7f 100644
--- a/tools/testing/selftests/bpf/test_verifier.c
+++ b/tools/testing/selftests/bpf/test_verifier.c
@@ -38,6 +38,7 @@
38 38
39#define MAX_INSNS 512 39#define MAX_INSNS 512
40#define MAX_FIXUPS 8 40#define MAX_FIXUPS 8
41#define MAX_NR_MAPS 4
41 42
42struct bpf_test { 43struct bpf_test {
43 const char *descr; 44 const char *descr;
@@ -45,6 +46,7 @@ struct bpf_test {
45 int fixup_map1[MAX_FIXUPS]; 46 int fixup_map1[MAX_FIXUPS];
46 int fixup_map2[MAX_FIXUPS]; 47 int fixup_map2[MAX_FIXUPS];
47 int fixup_prog[MAX_FIXUPS]; 48 int fixup_prog[MAX_FIXUPS];
49 int fixup_map_in_map[MAX_FIXUPS];
48 const char *errstr; 50 const char *errstr;
49 const char *errstr_unpriv; 51 const char *errstr_unpriv;
50 enum { 52 enum {
@@ -4452,7 +4454,76 @@ static struct bpf_test tests[] = {
4452 .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.", 4454 .errstr = "R0 min value is negative, either use unsigned index or do a if (index >=0) check.",
4453 .result = REJECT, 4455 .result = REJECT,
4454 .result_unpriv = REJECT, 4456 .result_unpriv = REJECT,
4455 } 4457 },
4458 {
4459 "map in map access",
4460 .insns = {
4461 BPF_ST_MEM(0, BPF_REG_10, -4, 0),
4462 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
4463 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
4464 BPF_LD_MAP_FD(BPF_REG_1, 0),
4465 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
4466 BPF_FUNC_map_lookup_elem),
4467 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 5),
4468 BPF_ST_MEM(0, BPF_REG_10, -4, 0),
4469 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
4470 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
4471 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
4472 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
4473 BPF_FUNC_map_lookup_elem),
4474 BPF_MOV64_REG(BPF_REG_0, 0),
4475 BPF_EXIT_INSN(),
4476 },
4477 .fixup_map_in_map = { 3 },
4478 .result = ACCEPT,
4479 },
4480 {
4481 "invalid inner map pointer",
4482 .insns = {
4483 BPF_ST_MEM(0, BPF_REG_10, -4, 0),
4484 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
4485 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
4486 BPF_LD_MAP_FD(BPF_REG_1, 0),
4487 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
4488 BPF_FUNC_map_lookup_elem),
4489 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 6),
4490 BPF_ST_MEM(0, BPF_REG_10, -4, 0),
4491 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
4492 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
4493 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
4494 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 8),
4495 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
4496 BPF_FUNC_map_lookup_elem),
4497 BPF_MOV64_REG(BPF_REG_0, 0),
4498 BPF_EXIT_INSN(),
4499 },
4500 .fixup_map_in_map = { 3 },
4501 .errstr = "R1 type=inv expected=map_ptr",
4502 .errstr_unpriv = "R1 pointer arithmetic prohibited",
4503 .result = REJECT,
4504 },
4505 {
4506 "forgot null checking on the inner map pointer",
4507 .insns = {
4508 BPF_ST_MEM(0, BPF_REG_10, -4, 0),
4509 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
4510 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
4511 BPF_LD_MAP_FD(BPF_REG_1, 0),
4512 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
4513 BPF_FUNC_map_lookup_elem),
4514 BPF_ST_MEM(0, BPF_REG_10, -4, 0),
4515 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
4516 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -4),
4517 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
4518 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0,
4519 BPF_FUNC_map_lookup_elem),
4520 BPF_MOV64_REG(BPF_REG_0, 0),
4521 BPF_EXIT_INSN(),
4522 },
4523 .fixup_map_in_map = { 3 },
4524 .errstr = "R1 type=map_value_or_null expected=map_ptr",
4525 .result = REJECT,
4526 },
4456}; 4527};
4457 4528
4458static int probe_filter_length(const struct bpf_insn *fp) 4529static int probe_filter_length(const struct bpf_insn *fp)
@@ -4489,42 +4560,73 @@ static int create_prog_array(void)
4489 return fd; 4560 return fd;
4490} 4561}
4491 4562
4563static int create_map_in_map(void)
4564{
4565 int inner_map_fd, outer_map_fd;
4566
4567 inner_map_fd = bpf_create_map(BPF_MAP_TYPE_ARRAY, sizeof(int),
4568 sizeof(int), 1, 0);
4569 if (inner_map_fd < 0) {
4570 printf("Failed to create array '%s'!\n", strerror(errno));
4571 return inner_map_fd;
4572 }
4573
4574 outer_map_fd = bpf_create_map_in_map(BPF_MAP_TYPE_ARRAY_OF_MAPS,
4575 sizeof(int), inner_map_fd, 1, 0);
4576 if (outer_map_fd < 0)
4577 printf("Failed to create array of maps '%s'!\n",
4578 strerror(errno));
4579
4580 close(inner_map_fd);
4581
4582 return outer_map_fd;
4583}
4584
4492static char bpf_vlog[32768]; 4585static char bpf_vlog[32768];
4493 4586
4494static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog, 4587static void do_test_fixup(struct bpf_test *test, struct bpf_insn *prog,
4495 int *fd_f1, int *fd_f2, int *fd_f3) 4588 int *map_fds)
4496{ 4589{
4497 int *fixup_map1 = test->fixup_map1; 4590 int *fixup_map1 = test->fixup_map1;
4498 int *fixup_map2 = test->fixup_map2; 4591 int *fixup_map2 = test->fixup_map2;
4499 int *fixup_prog = test->fixup_prog; 4592 int *fixup_prog = test->fixup_prog;
4593 int *fixup_map_in_map = test->fixup_map_in_map;
4500 4594
4501 /* Allocating HTs with 1 elem is fine here, since we only test 4595 /* Allocating HTs with 1 elem is fine here, since we only test
4502 * for verifier and not do a runtime lookup, so the only thing 4596 * for verifier and not do a runtime lookup, so the only thing
4503 * that really matters is value size in this case. 4597 * that really matters is value size in this case.
4504 */ 4598 */
4505 if (*fixup_map1) { 4599 if (*fixup_map1) {
4506 *fd_f1 = create_map(sizeof(long long), 1); 4600 map_fds[0] = create_map(sizeof(long long), 1);
4507 do { 4601 do {
4508 prog[*fixup_map1].imm = *fd_f1; 4602 prog[*fixup_map1].imm = map_fds[0];
4509 fixup_map1++; 4603 fixup_map1++;
4510 } while (*fixup_map1); 4604 } while (*fixup_map1);
4511 } 4605 }
4512 4606
4513 if (*fixup_map2) { 4607 if (*fixup_map2) {
4514 *fd_f2 = create_map(sizeof(struct test_val), 1); 4608 map_fds[1] = create_map(sizeof(struct test_val), 1);
4515 do { 4609 do {
4516 prog[*fixup_map2].imm = *fd_f2; 4610 prog[*fixup_map2].imm = map_fds[1];
4517 fixup_map2++; 4611 fixup_map2++;
4518 } while (*fixup_map2); 4612 } while (*fixup_map2);
4519 } 4613 }
4520 4614
4521 if (*fixup_prog) { 4615 if (*fixup_prog) {
4522 *fd_f3 = create_prog_array(); 4616 map_fds[2] = create_prog_array();
4523 do { 4617 do {
4524 prog[*fixup_prog].imm = *fd_f3; 4618 prog[*fixup_prog].imm = map_fds[2];
4525 fixup_prog++; 4619 fixup_prog++;
4526 } while (*fixup_prog); 4620 } while (*fixup_prog);
4527 } 4621 }
4622
4623 if (*fixup_map_in_map) {
4624 map_fds[3] = create_map_in_map();
4625 do {
4626 prog[*fixup_map_in_map].imm = map_fds[3];
4627 fixup_map_in_map++;
4628 } while (*fixup_map_in_map);
4629 }
4528} 4630}
4529 4631
4530static void do_test_single(struct bpf_test *test, bool unpriv, 4632static 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,
4533 struct bpf_insn *prog = test->insns; 4635 struct bpf_insn *prog = test->insns;
4534 int prog_len = probe_filter_length(prog); 4636 int prog_len = probe_filter_length(prog);
4535 int prog_type = test->prog_type; 4637 int prog_type = test->prog_type;
4536 int fd_f1 = -1, fd_f2 = -1, fd_f3 = -1; 4638 int map_fds[MAX_NR_MAPS];
4537 int fd_prog, expected_ret; 4639 int fd_prog, expected_ret;
4538 const char *expected_err; 4640 const char *expected_err;
4641 int i;
4642
4643 for (i = 0; i < MAX_NR_MAPS; i++)
4644 map_fds[i] = -1;
4539 4645
4540 do_test_fixup(test, prog, &fd_f1, &fd_f2, &fd_f3); 4646 do_test_fixup(test, prog, map_fds);
4541 4647
4542 fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, 4648 fd_prog = bpf_load_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
4543 prog, prog_len, "GPL", 0, bpf_vlog, 4649 prog, prog_len, "GPL", 0, bpf_vlog,
@@ -4568,9 +4674,8 @@ static void do_test_single(struct bpf_test *test, bool unpriv,
4568 printf("OK\n"); 4674 printf("OK\n");
4569close_fds: 4675close_fds:
4570 close(fd_prog); 4676 close(fd_prog);
4571 close(fd_f1); 4677 for (i = 0; i < MAX_NR_MAPS; i++)
4572 close(fd_f2); 4678 close(map_fds[i]);
4573 close(fd_f3);
4574 sched_yield(); 4679 sched_yield();
4575 return; 4680 return;
4576fail_log: 4681fail_log: