aboutsummaryrefslogtreecommitdiffstats
path: root/samples
diff options
context:
space:
mode:
Diffstat (limited to 'samples')
-rw-r--r--samples/bpf/Makefile12
-rw-r--r--samples/bpf/libbpf.c94
-rw-r--r--samples/bpf/libbpf.h172
-rw-r--r--samples/bpf/test_verifier.c689
4 files changed, 967 insertions, 0 deletions
diff --git a/samples/bpf/Makefile b/samples/bpf/Makefile
new file mode 100644
index 000000000000..634391797856
--- /dev/null
+++ b/samples/bpf/Makefile
@@ -0,0 +1,12 @@
1# kbuild trick to avoid linker error. Can be omitted if a module is built.
2obj- := dummy.o
3
4# List of programs to build
5hostprogs-y := test_verifier
6
7test_verifier-objs := test_verifier.o libbpf.o
8
9# Tell kbuild to always build the programs
10always := $(hostprogs-y)
11
12HOSTCFLAGS += -I$(objtree)/usr/include
diff --git a/samples/bpf/libbpf.c b/samples/bpf/libbpf.c
new file mode 100644
index 000000000000..ff6504420738
--- /dev/null
+++ b/samples/bpf/libbpf.c
@@ -0,0 +1,94 @@
1/* eBPF mini library */
2#include <stdlib.h>
3#include <stdio.h>
4#include <linux/unistd.h>
5#include <unistd.h>
6#include <string.h>
7#include <linux/netlink.h>
8#include <linux/bpf.h>
9#include <errno.h>
10#include "libbpf.h"
11
12static __u64 ptr_to_u64(void *ptr)
13{
14 return (__u64) (unsigned long) ptr;
15}
16
17int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
18 int max_entries)
19{
20 union bpf_attr attr = {
21 .map_type = map_type,
22 .key_size = key_size,
23 .value_size = value_size,
24 .max_entries = max_entries
25 };
26
27 return syscall(__NR_bpf, BPF_MAP_CREATE, &attr, sizeof(attr));
28}
29
30int bpf_update_elem(int fd, void *key, void *value)
31{
32 union bpf_attr attr = {
33 .map_fd = fd,
34 .key = ptr_to_u64(key),
35 .value = ptr_to_u64(value),
36 };
37
38 return syscall(__NR_bpf, BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
39}
40
41int bpf_lookup_elem(int fd, void *key, void *value)
42{
43 union bpf_attr attr = {
44 .map_fd = fd,
45 .key = ptr_to_u64(key),
46 .value = ptr_to_u64(value),
47 };
48
49 return syscall(__NR_bpf, BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
50}
51
52int bpf_delete_elem(int fd, void *key)
53{
54 union bpf_attr attr = {
55 .map_fd = fd,
56 .key = ptr_to_u64(key),
57 };
58
59 return syscall(__NR_bpf, BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
60}
61
62int bpf_get_next_key(int fd, void *key, void *next_key)
63{
64 union bpf_attr attr = {
65 .map_fd = fd,
66 .key = ptr_to_u64(key),
67 .next_key = ptr_to_u64(next_key),
68 };
69
70 return syscall(__NR_bpf, BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
71}
72
73#define ROUND_UP(x, n) (((x) + (n) - 1u) & ~((n) - 1u))
74
75char bpf_log_buf[LOG_BUF_SIZE];
76
77int bpf_prog_load(enum bpf_prog_type prog_type,
78 const struct bpf_insn *insns, int prog_len,
79 const char *license)
80{
81 union bpf_attr attr = {
82 .prog_type = prog_type,
83 .insns = ptr_to_u64((void *) insns),
84 .insn_cnt = prog_len / sizeof(struct bpf_insn),
85 .license = ptr_to_u64((void *) license),
86 .log_buf = ptr_to_u64(bpf_log_buf),
87 .log_size = LOG_BUF_SIZE,
88 .log_level = 1,
89 };
90
91 bpf_log_buf[0] = 0;
92
93 return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
94}
diff --git a/samples/bpf/libbpf.h b/samples/bpf/libbpf.h
new file mode 100644
index 000000000000..8a31babeca5d
--- /dev/null
+++ b/samples/bpf/libbpf.h
@@ -0,0 +1,172 @@
1/* eBPF mini library */
2#ifndef __LIBBPF_H
3#define __LIBBPF_H
4
5struct bpf_insn;
6
7int bpf_create_map(enum bpf_map_type map_type, int key_size, int value_size,
8 int max_entries);
9int bpf_update_elem(int fd, void *key, void *value);
10int bpf_lookup_elem(int fd, void *key, void *value);
11int bpf_delete_elem(int fd, void *key);
12int bpf_get_next_key(int fd, void *key, void *next_key);
13
14int bpf_prog_load(enum bpf_prog_type prog_type,
15 const struct bpf_insn *insns, int insn_len,
16 const char *license);
17
18#define LOG_BUF_SIZE 8192
19extern char bpf_log_buf[LOG_BUF_SIZE];
20
21/* ALU ops on registers, bpf_add|sub|...: dst_reg += src_reg */
22
23#define BPF_ALU64_REG(OP, DST, SRC) \
24 ((struct bpf_insn) { \
25 .code = BPF_ALU64 | BPF_OP(OP) | BPF_X, \
26 .dst_reg = DST, \
27 .src_reg = SRC, \
28 .off = 0, \
29 .imm = 0 })
30
31#define BPF_ALU32_REG(OP, DST, SRC) \
32 ((struct bpf_insn) { \
33 .code = BPF_ALU | BPF_OP(OP) | BPF_X, \
34 .dst_reg = DST, \
35 .src_reg = SRC, \
36 .off = 0, \
37 .imm = 0 })
38
39/* ALU ops on immediates, bpf_add|sub|...: dst_reg += imm32 */
40
41#define BPF_ALU64_IMM(OP, DST, IMM) \
42 ((struct bpf_insn) { \
43 .code = BPF_ALU64 | BPF_OP(OP) | BPF_K, \
44 .dst_reg = DST, \
45 .src_reg = 0, \
46 .off = 0, \
47 .imm = IMM })
48
49#define BPF_ALU32_IMM(OP, DST, IMM) \
50 ((struct bpf_insn) { \
51 .code = BPF_ALU | BPF_OP(OP) | BPF_K, \
52 .dst_reg = DST, \
53 .src_reg = 0, \
54 .off = 0, \
55 .imm = IMM })
56
57/* Short form of mov, dst_reg = src_reg */
58
59#define BPF_MOV64_REG(DST, SRC) \
60 ((struct bpf_insn) { \
61 .code = BPF_ALU64 | BPF_MOV | BPF_X, \
62 .dst_reg = DST, \
63 .src_reg = SRC, \
64 .off = 0, \
65 .imm = 0 })
66
67/* Short form of mov, dst_reg = imm32 */
68
69#define BPF_MOV64_IMM(DST, IMM) \
70 ((struct bpf_insn) { \
71 .code = BPF_ALU64 | BPF_MOV | BPF_K, \
72 .dst_reg = DST, \
73 .src_reg = 0, \
74 .off = 0, \
75 .imm = IMM })
76
77/* BPF_LD_IMM64 macro encodes single 'load 64-bit immediate' insn */
78#define BPF_LD_IMM64(DST, IMM) \
79 BPF_LD_IMM64_RAW(DST, 0, IMM)
80
81#define BPF_LD_IMM64_RAW(DST, SRC, IMM) \
82 ((struct bpf_insn) { \
83 .code = BPF_LD | BPF_DW | BPF_IMM, \
84 .dst_reg = DST, \
85 .src_reg = SRC, \
86 .off = 0, \
87 .imm = (__u32) (IMM) }), \
88 ((struct bpf_insn) { \
89 .code = 0, /* zero is reserved opcode */ \
90 .dst_reg = 0, \
91 .src_reg = 0, \
92 .off = 0, \
93 .imm = ((__u64) (IMM)) >> 32 })
94
95#define BPF_PSEUDO_MAP_FD 1
96
97/* pseudo BPF_LD_IMM64 insn used to refer to process-local map_fd */
98#define BPF_LD_MAP_FD(DST, MAP_FD) \
99 BPF_LD_IMM64_RAW(DST, BPF_PSEUDO_MAP_FD, MAP_FD)
100
101
102/* Memory load, dst_reg = *(uint *) (src_reg + off16) */
103
104#define BPF_LDX_MEM(SIZE, DST, SRC, OFF) \
105 ((struct bpf_insn) { \
106 .code = BPF_LDX | BPF_SIZE(SIZE) | BPF_MEM, \
107 .dst_reg = DST, \
108 .src_reg = SRC, \
109 .off = OFF, \
110 .imm = 0 })
111
112/* Memory store, *(uint *) (dst_reg + off16) = src_reg */
113
114#define BPF_STX_MEM(SIZE, DST, SRC, OFF) \
115 ((struct bpf_insn) { \
116 .code = BPF_STX | BPF_SIZE(SIZE) | BPF_MEM, \
117 .dst_reg = DST, \
118 .src_reg = SRC, \
119 .off = OFF, \
120 .imm = 0 })
121
122/* Memory store, *(uint *) (dst_reg + off16) = imm32 */
123
124#define BPF_ST_MEM(SIZE, DST, OFF, IMM) \
125 ((struct bpf_insn) { \
126 .code = BPF_ST | BPF_SIZE(SIZE) | BPF_MEM, \
127 .dst_reg = DST, \
128 .src_reg = 0, \
129 .off = OFF, \
130 .imm = IMM })
131
132/* Conditional jumps against registers, if (dst_reg 'op' src_reg) goto pc + off16 */
133
134#define BPF_JMP_REG(OP, DST, SRC, OFF) \
135 ((struct bpf_insn) { \
136 .code = BPF_JMP | BPF_OP(OP) | BPF_X, \
137 .dst_reg = DST, \
138 .src_reg = SRC, \
139 .off = OFF, \
140 .imm = 0 })
141
142/* Conditional jumps against immediates, if (dst_reg 'op' imm32) goto pc + off16 */
143
144#define BPF_JMP_IMM(OP, DST, IMM, OFF) \
145 ((struct bpf_insn) { \
146 .code = BPF_JMP | BPF_OP(OP) | BPF_K, \
147 .dst_reg = DST, \
148 .src_reg = 0, \
149 .off = OFF, \
150 .imm = IMM })
151
152/* Raw code statement block */
153
154#define BPF_RAW_INSN(CODE, DST, SRC, OFF, IMM) \
155 ((struct bpf_insn) { \
156 .code = CODE, \
157 .dst_reg = DST, \
158 .src_reg = SRC, \
159 .off = OFF, \
160 .imm = IMM })
161
162/* Program exit */
163
164#define BPF_EXIT_INSN() \
165 ((struct bpf_insn) { \
166 .code = BPF_JMP | BPF_EXIT, \
167 .dst_reg = 0, \
168 .src_reg = 0, \
169 .off = 0, \
170 .imm = 0 })
171
172#endif
diff --git a/samples/bpf/test_verifier.c b/samples/bpf/test_verifier.c
new file mode 100644
index 000000000000..eb4bec0ad8af
--- /dev/null
+++ b/samples/bpf/test_verifier.c
@@ -0,0 +1,689 @@
1/*
2 * Testsuite for eBPF verifier
3 *
4 * Copyright (c) 2014 PLUMgrid, http://plumgrid.com
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of version 2 of the GNU General Public
8 * License as published by the Free Software Foundation.
9 */
10#include <stdio.h>
11#include <unistd.h>
12#include <linux/bpf.h>
13#include <errno.h>
14#include <linux/unistd.h>
15#include <string.h>
16#include <linux/filter.h>
17#include "libbpf.h"
18
19#define MAX_INSNS 512
20#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
21
22struct bpf_test {
23 const char *descr;
24 struct bpf_insn insns[MAX_INSNS];
25 int fixup[32];
26 const char *errstr;
27 enum {
28 ACCEPT,
29 REJECT
30 } result;
31};
32
33static struct bpf_test tests[] = {
34 {
35 "add+sub+mul",
36 .insns = {
37 BPF_MOV64_IMM(BPF_REG_1, 1),
38 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 2),
39 BPF_MOV64_IMM(BPF_REG_2, 3),
40 BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_2),
41 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -1),
42 BPF_ALU64_IMM(BPF_MUL, BPF_REG_1, 3),
43 BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
44 BPF_EXIT_INSN(),
45 },
46 .result = ACCEPT,
47 },
48 {
49 "unreachable",
50 .insns = {
51 BPF_EXIT_INSN(),
52 BPF_EXIT_INSN(),
53 },
54 .errstr = "unreachable",
55 .result = REJECT,
56 },
57 {
58 "unreachable2",
59 .insns = {
60 BPF_JMP_IMM(BPF_JA, 0, 0, 1),
61 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
62 BPF_EXIT_INSN(),
63 },
64 .errstr = "unreachable",
65 .result = REJECT,
66 },
67 {
68 "out of range jump",
69 .insns = {
70 BPF_JMP_IMM(BPF_JA, 0, 0, 1),
71 BPF_EXIT_INSN(),
72 },
73 .errstr = "jump out of range",
74 .result = REJECT,
75 },
76 {
77 "out of range jump2",
78 .insns = {
79 BPF_JMP_IMM(BPF_JA, 0, 0, -2),
80 BPF_EXIT_INSN(),
81 },
82 .errstr = "jump out of range",
83 .result = REJECT,
84 },
85 {
86 "test1 ld_imm64",
87 .insns = {
88 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
89 BPF_LD_IMM64(BPF_REG_0, 0),
90 BPF_LD_IMM64(BPF_REG_0, 0),
91 BPF_LD_IMM64(BPF_REG_0, 1),
92 BPF_LD_IMM64(BPF_REG_0, 1),
93 BPF_MOV64_IMM(BPF_REG_0, 2),
94 BPF_EXIT_INSN(),
95 },
96 .errstr = "invalid BPF_LD_IMM insn",
97 .result = REJECT,
98 },
99 {
100 "test2 ld_imm64",
101 .insns = {
102 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
103 BPF_LD_IMM64(BPF_REG_0, 0),
104 BPF_LD_IMM64(BPF_REG_0, 0),
105 BPF_LD_IMM64(BPF_REG_0, 1),
106 BPF_LD_IMM64(BPF_REG_0, 1),
107 BPF_EXIT_INSN(),
108 },
109 .errstr = "invalid BPF_LD_IMM insn",
110 .result = REJECT,
111 },
112 {
113 "test3 ld_imm64",
114 .insns = {
115 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
116 BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
117 BPF_LD_IMM64(BPF_REG_0, 0),
118 BPF_LD_IMM64(BPF_REG_0, 0),
119 BPF_LD_IMM64(BPF_REG_0, 1),
120 BPF_LD_IMM64(BPF_REG_0, 1),
121 BPF_EXIT_INSN(),
122 },
123 .errstr = "invalid bpf_ld_imm64 insn",
124 .result = REJECT,
125 },
126 {
127 "test4 ld_imm64",
128 .insns = {
129 BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
130 BPF_EXIT_INSN(),
131 },
132 .errstr = "invalid bpf_ld_imm64 insn",
133 .result = REJECT,
134 },
135 {
136 "test5 ld_imm64",
137 .insns = {
138 BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
139 },
140 .errstr = "invalid bpf_ld_imm64 insn",
141 .result = REJECT,
142 },
143 {
144 "no bpf_exit",
145 .insns = {
146 BPF_ALU64_REG(BPF_MOV, BPF_REG_0, BPF_REG_2),
147 },
148 .errstr = "jump out of range",
149 .result = REJECT,
150 },
151 {
152 "loop (back-edge)",
153 .insns = {
154 BPF_JMP_IMM(BPF_JA, 0, 0, -1),
155 BPF_EXIT_INSN(),
156 },
157 .errstr = "back-edge",
158 .result = REJECT,
159 },
160 {
161 "loop2 (back-edge)",
162 .insns = {
163 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
164 BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
165 BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
166 BPF_JMP_IMM(BPF_JA, 0, 0, -4),
167 BPF_EXIT_INSN(),
168 },
169 .errstr = "back-edge",
170 .result = REJECT,
171 },
172 {
173 "conditional loop",
174 .insns = {
175 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
176 BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
177 BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
178 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3),
179 BPF_EXIT_INSN(),
180 },
181 .errstr = "back-edge",
182 .result = REJECT,
183 },
184 {
185 "read uninitialized register",
186 .insns = {
187 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
188 BPF_EXIT_INSN(),
189 },
190 .errstr = "R2 !read_ok",
191 .result = REJECT,
192 },
193 {
194 "read invalid register",
195 .insns = {
196 BPF_MOV64_REG(BPF_REG_0, -1),
197 BPF_EXIT_INSN(),
198 },
199 .errstr = "R15 is invalid",
200 .result = REJECT,
201 },
202 {
203 "program doesn't init R0 before exit",
204 .insns = {
205 BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
206 BPF_EXIT_INSN(),
207 },
208 .errstr = "R0 !read_ok",
209 .result = REJECT,
210 },
211 {
212 "program doesn't init R0 before exit in all branches",
213 .insns = {
214 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
215 BPF_MOV64_IMM(BPF_REG_0, 1),
216 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2),
217 BPF_EXIT_INSN(),
218 },
219 .errstr = "R0 !read_ok",
220 .result = REJECT,
221 },
222 {
223 "stack out of bounds",
224 .insns = {
225 BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
226 BPF_EXIT_INSN(),
227 },
228 .errstr = "invalid stack",
229 .result = REJECT,
230 },
231 {
232 "invalid call insn1",
233 .insns = {
234 BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0),
235 BPF_EXIT_INSN(),
236 },
237 .errstr = "BPF_CALL uses reserved",
238 .result = REJECT,
239 },
240 {
241 "invalid call insn2",
242 .insns = {
243 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 1, 0),
244 BPF_EXIT_INSN(),
245 },
246 .errstr = "BPF_CALL uses reserved",
247 .result = REJECT,
248 },
249 {
250 "invalid function call",
251 .insns = {
252 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 1234567),
253 BPF_EXIT_INSN(),
254 },
255 .errstr = "invalid func 1234567",
256 .result = REJECT,
257 },
258 {
259 "uninitialized stack1",
260 .insns = {
261 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
262 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
263 BPF_LD_MAP_FD(BPF_REG_1, 0),
264 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
265 BPF_EXIT_INSN(),
266 },
267 .fixup = {2},
268 .errstr = "invalid indirect read from stack",
269 .result = REJECT,
270 },
271 {
272 "uninitialized stack2",
273 .insns = {
274 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
275 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -8),
276 BPF_EXIT_INSN(),
277 },
278 .errstr = "invalid read from stack",
279 .result = REJECT,
280 },
281 {
282 "check valid spill/fill",
283 .insns = {
284 /* spill R1(ctx) into stack */
285 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
286
287 /* fill it back into R2 */
288 BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8),
289
290 /* should be able to access R0 = *(R2 + 8) */
291 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 8),
292 BPF_EXIT_INSN(),
293 },
294 .result = ACCEPT,
295 },
296 {
297 "check corrupted spill/fill",
298 .insns = {
299 /* spill R1(ctx) into stack */
300 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
301
302 /* mess up with R1 pointer on stack */
303 BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23),
304
305 /* fill back into R0 should fail */
306 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
307
308 BPF_EXIT_INSN(),
309 },
310 .errstr = "corrupted spill",
311 .result = REJECT,
312 },
313 {
314 "invalid src register in STX",
315 .insns = {
316 BPF_STX_MEM(BPF_B, BPF_REG_10, -1, -1),
317 BPF_EXIT_INSN(),
318 },
319 .errstr = "R15 is invalid",
320 .result = REJECT,
321 },
322 {
323 "invalid dst register in STX",
324 .insns = {
325 BPF_STX_MEM(BPF_B, 14, BPF_REG_10, -1),
326 BPF_EXIT_INSN(),
327 },
328 .errstr = "R14 is invalid",
329 .result = REJECT,
330 },
331 {
332 "invalid dst register in ST",
333 .insns = {
334 BPF_ST_MEM(BPF_B, 14, -1, -1),
335 BPF_EXIT_INSN(),
336 },
337 .errstr = "R14 is invalid",
338 .result = REJECT,
339 },
340 {
341 "invalid src register in LDX",
342 .insns = {
343 BPF_LDX_MEM(BPF_B, BPF_REG_0, 12, 0),
344 BPF_EXIT_INSN(),
345 },
346 .errstr = "R12 is invalid",
347 .result = REJECT,
348 },
349 {
350 "invalid dst register in LDX",
351 .insns = {
352 BPF_LDX_MEM(BPF_B, 11, BPF_REG_1, 0),
353 BPF_EXIT_INSN(),
354 },
355 .errstr = "R11 is invalid",
356 .result = REJECT,
357 },
358 {
359 "junk insn",
360 .insns = {
361 BPF_RAW_INSN(0, 0, 0, 0, 0),
362 BPF_EXIT_INSN(),
363 },
364 .errstr = "invalid BPF_LD_IMM",
365 .result = REJECT,
366 },
367 {
368 "junk insn2",
369 .insns = {
370 BPF_RAW_INSN(1, 0, 0, 0, 0),
371 BPF_EXIT_INSN(),
372 },
373 .errstr = "BPF_LDX uses reserved fields",
374 .result = REJECT,
375 },
376 {
377 "junk insn3",
378 .insns = {
379 BPF_RAW_INSN(-1, 0, 0, 0, 0),
380 BPF_EXIT_INSN(),
381 },
382 .errstr = "invalid BPF_ALU opcode f0",
383 .result = REJECT,
384 },
385 {
386 "junk insn4",
387 .insns = {
388 BPF_RAW_INSN(-1, -1, -1, -1, -1),
389 BPF_EXIT_INSN(),
390 },
391 .errstr = "invalid BPF_ALU opcode f0",
392 .result = REJECT,
393 },
394 {
395 "junk insn5",
396 .insns = {
397 BPF_RAW_INSN(0x7f, -1, -1, -1, -1),
398 BPF_EXIT_INSN(),
399 },
400 .errstr = "BPF_ALU uses reserved fields",
401 .result = REJECT,
402 },
403 {
404 "misaligned read from stack",
405 .insns = {
406 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
407 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -4),
408 BPF_EXIT_INSN(),
409 },
410 .errstr = "misaligned access",
411 .result = REJECT,
412 },
413 {
414 "invalid map_fd for function call",
415 .insns = {
416 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
417 BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_10),
418 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
419 BPF_LD_MAP_FD(BPF_REG_1, 0),
420 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
421 BPF_EXIT_INSN(),
422 },
423 .errstr = "fd 0 is not pointing to valid bpf_map",
424 .result = REJECT,
425 },
426 {
427 "don't check return value before access",
428 .insns = {
429 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
430 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
431 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
432 BPF_LD_MAP_FD(BPF_REG_1, 0),
433 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
434 BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
435 BPF_EXIT_INSN(),
436 },
437 .fixup = {3},
438 .errstr = "R0 invalid mem access 'map_value_or_null'",
439 .result = REJECT,
440 },
441 {
442 "access memory with incorrect alignment",
443 .insns = {
444 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
445 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
446 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
447 BPF_LD_MAP_FD(BPF_REG_1, 0),
448 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
449 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
450 BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
451 BPF_EXIT_INSN(),
452 },
453 .fixup = {3},
454 .errstr = "misaligned access",
455 .result = REJECT,
456 },
457 {
458 "sometimes access memory with incorrect alignment",
459 .insns = {
460 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
461 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
462 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
463 BPF_LD_MAP_FD(BPF_REG_1, 0),
464 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
465 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
466 BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
467 BPF_EXIT_INSN(),
468 BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
469 BPF_EXIT_INSN(),
470 },
471 .fixup = {3},
472 .errstr = "R0 invalid mem access",
473 .result = REJECT,
474 },
475 {
476 "jump test 1",
477 .insns = {
478 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
479 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -8),
480 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
481 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
482 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1),
483 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 1),
484 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 1),
485 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 2),
486 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 1),
487 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 3),
488 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1),
489 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 4),
490 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1),
491 BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 5),
492 BPF_MOV64_IMM(BPF_REG_0, 0),
493 BPF_EXIT_INSN(),
494 },
495 .result = ACCEPT,
496 },
497 {
498 "jump test 2",
499 .insns = {
500 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
501 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 2),
502 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
503 BPF_JMP_IMM(BPF_JA, 0, 0, 14),
504 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 2),
505 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0),
506 BPF_JMP_IMM(BPF_JA, 0, 0, 11),
507 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 2),
508 BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0),
509 BPF_JMP_IMM(BPF_JA, 0, 0, 8),
510 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
511 BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0),
512 BPF_JMP_IMM(BPF_JA, 0, 0, 5),
513 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 2),
514 BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0),
515 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
516 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1),
517 BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0),
518 BPF_MOV64_IMM(BPF_REG_0, 0),
519 BPF_EXIT_INSN(),
520 },
521 .result = ACCEPT,
522 },
523 {
524 "jump test 3",
525 .insns = {
526 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
527 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3),
528 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
529 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
530 BPF_JMP_IMM(BPF_JA, 0, 0, 19),
531 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 3),
532 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0),
533 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16),
534 BPF_JMP_IMM(BPF_JA, 0, 0, 15),
535 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 3),
536 BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0),
537 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32),
538 BPF_JMP_IMM(BPF_JA, 0, 0, 11),
539 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 3),
540 BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0),
541 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -40),
542 BPF_JMP_IMM(BPF_JA, 0, 0, 7),
543 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 3),
544 BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0),
545 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -48),
546 BPF_JMP_IMM(BPF_JA, 0, 0, 3),
547 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 0),
548 BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0),
549 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -56),
550 BPF_LD_MAP_FD(BPF_REG_1, 0),
551 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_unspec),
552 BPF_EXIT_INSN(),
553 },
554 .fixup = {24},
555 .result = ACCEPT,
556 },
557 {
558 "jump test 4",
559 .insns = {
560 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
561 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
562 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
563 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
564 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
565 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
566 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
567 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
568 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
569 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
570 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
571 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
572 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
573 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
574 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
575 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
576 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
577 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
578 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
579 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
580 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
581 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
582 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
583 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
584 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
585 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
586 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
587 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
588 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
589 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
590 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
591 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
592 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
593 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
594 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
595 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
596 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
597 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
598 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
599 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
600 BPF_MOV64_IMM(BPF_REG_0, 0),
601 BPF_EXIT_INSN(),
602 },
603 .result = ACCEPT,
604 },
605};
606
607static int probe_filter_length(struct bpf_insn *fp)
608{
609 int len = 0;
610
611 for (len = MAX_INSNS - 1; len > 0; --len)
612 if (fp[len].code != 0 || fp[len].imm != 0)
613 break;
614
615 return len + 1;
616}
617
618static int create_map(void)
619{
620 long long key, value = 0;
621 int map_fd;
622
623 map_fd = bpf_create_map(BPF_MAP_TYPE_UNSPEC, sizeof(key), sizeof(value), 1024);
624 if (map_fd < 0) {
625 printf("failed to create map '%s'\n", strerror(errno));
626 }
627
628 return map_fd;
629}
630
631static int test(void)
632{
633 int prog_fd, i;
634
635 for (i = 0; i < ARRAY_SIZE(tests); i++) {
636 struct bpf_insn *prog = tests[i].insns;
637 int prog_len = probe_filter_length(prog);
638 int *fixup = tests[i].fixup;
639 int map_fd = -1;
640
641 if (*fixup) {
642 map_fd = create_map();
643
644 do {
645 prog[*fixup].imm = map_fd;
646 fixup++;
647 } while (*fixup);
648 }
649 printf("#%d %s ", i, tests[i].descr);
650
651 prog_fd = bpf_prog_load(BPF_PROG_TYPE_UNSPEC, prog,
652 prog_len * sizeof(struct bpf_insn),
653 "GPL");
654
655 if (tests[i].result == ACCEPT) {
656 if (prog_fd < 0) {
657 printf("FAIL\nfailed to load prog '%s'\n",
658 strerror(errno));
659 printf("%s", bpf_log_buf);
660 goto fail;
661 }
662 } else {
663 if (prog_fd >= 0) {
664 printf("FAIL\nunexpected success to load\n");
665 printf("%s", bpf_log_buf);
666 goto fail;
667 }
668 if (strstr(bpf_log_buf, tests[i].errstr) == 0) {
669 printf("FAIL\nunexpected error message: %s",
670 bpf_log_buf);
671 goto fail;
672 }
673 }
674
675 printf("OK\n");
676fail:
677 if (map_fd >= 0)
678 close(map_fd);
679 close(prog_fd);
680
681 }
682
683 return 0;
684}
685
686int main(void)
687{
688 return test();
689}