blob: ee0f110c9c543b54fd8593607d161598fb29e472 [file] [log] [blame]
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -07001/*
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>
Alexei Starovoitov614cd3b2015-03-13 11:57:43 -070017#include <stddef.h>
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -070018#include "libbpf.h"
19
20#define MAX_INSNS 512
21#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
22
23struct bpf_test {
24 const char *descr;
25 struct bpf_insn insns[MAX_INSNS];
26 int fixup[32];
27 const char *errstr;
28 enum {
29 ACCEPT,
30 REJECT
31 } result;
Alexei Starovoitovd691f9e2015-06-04 10:11:54 -070032 enum bpf_prog_type prog_type;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -070033};
34
35static struct bpf_test tests[] = {
36 {
37 "add+sub+mul",
38 .insns = {
39 BPF_MOV64_IMM(BPF_REG_1, 1),
40 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, 2),
41 BPF_MOV64_IMM(BPF_REG_2, 3),
42 BPF_ALU64_REG(BPF_SUB, BPF_REG_1, BPF_REG_2),
43 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -1),
44 BPF_ALU64_IMM(BPF_MUL, BPF_REG_1, 3),
45 BPF_MOV64_REG(BPF_REG_0, BPF_REG_1),
46 BPF_EXIT_INSN(),
47 },
48 .result = ACCEPT,
49 },
50 {
51 "unreachable",
52 .insns = {
53 BPF_EXIT_INSN(),
54 BPF_EXIT_INSN(),
55 },
56 .errstr = "unreachable",
57 .result = REJECT,
58 },
59 {
60 "unreachable2",
61 .insns = {
62 BPF_JMP_IMM(BPF_JA, 0, 0, 1),
63 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
64 BPF_EXIT_INSN(),
65 },
66 .errstr = "unreachable",
67 .result = REJECT,
68 },
69 {
70 "out of range jump",
71 .insns = {
72 BPF_JMP_IMM(BPF_JA, 0, 0, 1),
73 BPF_EXIT_INSN(),
74 },
75 .errstr = "jump out of range",
76 .result = REJECT,
77 },
78 {
79 "out of range jump2",
80 .insns = {
81 BPF_JMP_IMM(BPF_JA, 0, 0, -2),
82 BPF_EXIT_INSN(),
83 },
84 .errstr = "jump out of range",
85 .result = REJECT,
86 },
87 {
88 "test1 ld_imm64",
89 .insns = {
90 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
91 BPF_LD_IMM64(BPF_REG_0, 0),
92 BPF_LD_IMM64(BPF_REG_0, 0),
93 BPF_LD_IMM64(BPF_REG_0, 1),
94 BPF_LD_IMM64(BPF_REG_0, 1),
95 BPF_MOV64_IMM(BPF_REG_0, 2),
96 BPF_EXIT_INSN(),
97 },
98 .errstr = "invalid BPF_LD_IMM insn",
99 .result = REJECT,
100 },
101 {
102 "test2 ld_imm64",
103 .insns = {
104 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
105 BPF_LD_IMM64(BPF_REG_0, 0),
106 BPF_LD_IMM64(BPF_REG_0, 0),
107 BPF_LD_IMM64(BPF_REG_0, 1),
108 BPF_LD_IMM64(BPF_REG_0, 1),
109 BPF_EXIT_INSN(),
110 },
111 .errstr = "invalid BPF_LD_IMM insn",
112 .result = REJECT,
113 },
114 {
115 "test3 ld_imm64",
116 .insns = {
117 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
118 BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
119 BPF_LD_IMM64(BPF_REG_0, 0),
120 BPF_LD_IMM64(BPF_REG_0, 0),
121 BPF_LD_IMM64(BPF_REG_0, 1),
122 BPF_LD_IMM64(BPF_REG_0, 1),
123 BPF_EXIT_INSN(),
124 },
125 .errstr = "invalid bpf_ld_imm64 insn",
126 .result = REJECT,
127 },
128 {
129 "test4 ld_imm64",
130 .insns = {
131 BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
132 BPF_EXIT_INSN(),
133 },
134 .errstr = "invalid bpf_ld_imm64 insn",
135 .result = REJECT,
136 },
137 {
138 "test5 ld_imm64",
139 .insns = {
140 BPF_RAW_INSN(BPF_LD | BPF_IMM | BPF_DW, 0, 0, 0, 0),
141 },
142 .errstr = "invalid bpf_ld_imm64 insn",
143 .result = REJECT,
144 },
145 {
146 "no bpf_exit",
147 .insns = {
148 BPF_ALU64_REG(BPF_MOV, BPF_REG_0, BPF_REG_2),
149 },
150 .errstr = "jump out of range",
151 .result = REJECT,
152 },
153 {
154 "loop (back-edge)",
155 .insns = {
156 BPF_JMP_IMM(BPF_JA, 0, 0, -1),
157 BPF_EXIT_INSN(),
158 },
159 .errstr = "back-edge",
160 .result = REJECT,
161 },
162 {
163 "loop2 (back-edge)",
164 .insns = {
165 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
166 BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
167 BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
168 BPF_JMP_IMM(BPF_JA, 0, 0, -4),
169 BPF_EXIT_INSN(),
170 },
171 .errstr = "back-edge",
172 .result = REJECT,
173 },
174 {
175 "conditional loop",
176 .insns = {
177 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
178 BPF_MOV64_REG(BPF_REG_2, BPF_REG_0),
179 BPF_MOV64_REG(BPF_REG_3, BPF_REG_0),
180 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, -3),
181 BPF_EXIT_INSN(),
182 },
183 .errstr = "back-edge",
184 .result = REJECT,
185 },
186 {
187 "read uninitialized register",
188 .insns = {
189 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
190 BPF_EXIT_INSN(),
191 },
192 .errstr = "R2 !read_ok",
193 .result = REJECT,
194 },
195 {
196 "read invalid register",
197 .insns = {
198 BPF_MOV64_REG(BPF_REG_0, -1),
199 BPF_EXIT_INSN(),
200 },
201 .errstr = "R15 is invalid",
202 .result = REJECT,
203 },
204 {
205 "program doesn't init R0 before exit",
206 .insns = {
207 BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_1),
208 BPF_EXIT_INSN(),
209 },
210 .errstr = "R0 !read_ok",
211 .result = REJECT,
212 },
213 {
Alexei Starovoitov32bf08a2014-10-20 14:54:57 -0700214 "program doesn't init R0 before exit in all branches",
215 .insns = {
216 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
217 BPF_MOV64_IMM(BPF_REG_0, 1),
218 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 2),
219 BPF_EXIT_INSN(),
220 },
221 .errstr = "R0 !read_ok",
222 .result = REJECT,
223 },
224 {
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700225 "stack out of bounds",
226 .insns = {
227 BPF_ST_MEM(BPF_DW, BPF_REG_10, 8, 0),
228 BPF_EXIT_INSN(),
229 },
230 .errstr = "invalid stack",
231 .result = REJECT,
232 },
233 {
234 "invalid call insn1",
235 .insns = {
236 BPF_RAW_INSN(BPF_JMP | BPF_CALL | BPF_X, 0, 0, 0, 0),
237 BPF_EXIT_INSN(),
238 },
239 .errstr = "BPF_CALL uses reserved",
240 .result = REJECT,
241 },
242 {
243 "invalid call insn2",
244 .insns = {
245 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 1, 0),
246 BPF_EXIT_INSN(),
247 },
248 .errstr = "BPF_CALL uses reserved",
249 .result = REJECT,
250 },
251 {
252 "invalid function call",
253 .insns = {
254 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, 1234567),
255 BPF_EXIT_INSN(),
256 },
257 .errstr = "invalid func 1234567",
258 .result = REJECT,
259 },
260 {
261 "uninitialized stack1",
262 .insns = {
263 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
264 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
265 BPF_LD_MAP_FD(BPF_REG_1, 0),
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800266 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700267 BPF_EXIT_INSN(),
268 },
269 .fixup = {2},
270 .errstr = "invalid indirect read from stack",
271 .result = REJECT,
272 },
273 {
274 "uninitialized stack2",
275 .insns = {
276 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
277 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -8),
278 BPF_EXIT_INSN(),
279 },
280 .errstr = "invalid read from stack",
281 .result = REJECT,
282 },
283 {
284 "check valid spill/fill",
285 .insns = {
286 /* spill R1(ctx) into stack */
287 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
288
289 /* fill it back into R2 */
290 BPF_LDX_MEM(BPF_DW, BPF_REG_2, BPF_REG_10, -8),
291
292 /* should be able to access R0 = *(R2 + 8) */
Daniel Borkmannf91fe172015-03-01 12:31:41 +0100293 /* BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, 8), */
294 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2),
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700295 BPF_EXIT_INSN(),
296 },
297 .result = ACCEPT,
298 },
299 {
300 "check corrupted spill/fill",
301 .insns = {
302 /* spill R1(ctx) into stack */
303 BPF_STX_MEM(BPF_DW, BPF_REG_10, BPF_REG_1, -8),
304
305 /* mess up with R1 pointer on stack */
306 BPF_ST_MEM(BPF_B, BPF_REG_10, -7, 0x23),
307
308 /* fill back into R0 should fail */
309 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_10, -8),
310
311 BPF_EXIT_INSN(),
312 },
313 .errstr = "corrupted spill",
314 .result = REJECT,
315 },
316 {
317 "invalid src register in STX",
318 .insns = {
319 BPF_STX_MEM(BPF_B, BPF_REG_10, -1, -1),
320 BPF_EXIT_INSN(),
321 },
322 .errstr = "R15 is invalid",
323 .result = REJECT,
324 },
325 {
326 "invalid dst register in STX",
327 .insns = {
328 BPF_STX_MEM(BPF_B, 14, BPF_REG_10, -1),
329 BPF_EXIT_INSN(),
330 },
331 .errstr = "R14 is invalid",
332 .result = REJECT,
333 },
334 {
335 "invalid dst register in ST",
336 .insns = {
337 BPF_ST_MEM(BPF_B, 14, -1, -1),
338 BPF_EXIT_INSN(),
339 },
340 .errstr = "R14 is invalid",
341 .result = REJECT,
342 },
343 {
344 "invalid src register in LDX",
345 .insns = {
346 BPF_LDX_MEM(BPF_B, BPF_REG_0, 12, 0),
347 BPF_EXIT_INSN(),
348 },
349 .errstr = "R12 is invalid",
350 .result = REJECT,
351 },
352 {
353 "invalid dst register in LDX",
354 .insns = {
355 BPF_LDX_MEM(BPF_B, 11, BPF_REG_1, 0),
356 BPF_EXIT_INSN(),
357 },
358 .errstr = "R11 is invalid",
359 .result = REJECT,
360 },
361 {
362 "junk insn",
363 .insns = {
364 BPF_RAW_INSN(0, 0, 0, 0, 0),
365 BPF_EXIT_INSN(),
366 },
367 .errstr = "invalid BPF_LD_IMM",
368 .result = REJECT,
369 },
370 {
371 "junk insn2",
372 .insns = {
373 BPF_RAW_INSN(1, 0, 0, 0, 0),
374 BPF_EXIT_INSN(),
375 },
376 .errstr = "BPF_LDX uses reserved fields",
377 .result = REJECT,
378 },
379 {
380 "junk insn3",
381 .insns = {
382 BPF_RAW_INSN(-1, 0, 0, 0, 0),
383 BPF_EXIT_INSN(),
384 },
385 .errstr = "invalid BPF_ALU opcode f0",
386 .result = REJECT,
387 },
388 {
389 "junk insn4",
390 .insns = {
391 BPF_RAW_INSN(-1, -1, -1, -1, -1),
392 BPF_EXIT_INSN(),
393 },
394 .errstr = "invalid BPF_ALU opcode f0",
395 .result = REJECT,
396 },
397 {
398 "junk insn5",
399 .insns = {
400 BPF_RAW_INSN(0x7f, -1, -1, -1, -1),
401 BPF_EXIT_INSN(),
402 },
403 .errstr = "BPF_ALU uses reserved fields",
404 .result = REJECT,
405 },
406 {
407 "misaligned read from stack",
408 .insns = {
409 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
410 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_2, -4),
411 BPF_EXIT_INSN(),
412 },
413 .errstr = "misaligned access",
414 .result = REJECT,
415 },
416 {
417 "invalid map_fd for function call",
418 .insns = {
419 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
420 BPF_ALU64_REG(BPF_MOV, BPF_REG_2, BPF_REG_10),
421 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
422 BPF_LD_MAP_FD(BPF_REG_1, 0),
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800423 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_delete_elem),
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700424 BPF_EXIT_INSN(),
425 },
426 .errstr = "fd 0 is not pointing to valid bpf_map",
427 .result = REJECT,
428 },
429 {
430 "don't check return value before access",
431 .insns = {
432 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
433 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
434 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
435 BPF_LD_MAP_FD(BPF_REG_1, 0),
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800436 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700437 BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
438 BPF_EXIT_INSN(),
439 },
440 .fixup = {3},
441 .errstr = "R0 invalid mem access 'map_value_or_null'",
442 .result = REJECT,
443 },
444 {
445 "access memory with incorrect alignment",
446 .insns = {
447 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
448 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
449 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
450 BPF_LD_MAP_FD(BPF_REG_1, 0),
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800451 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700452 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 1),
453 BPF_ST_MEM(BPF_DW, BPF_REG_0, 4, 0),
454 BPF_EXIT_INSN(),
455 },
456 .fixup = {3},
457 .errstr = "misaligned access",
458 .result = REJECT,
459 },
460 {
461 "sometimes access memory with incorrect alignment",
462 .insns = {
463 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
464 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
465 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
466 BPF_LD_MAP_FD(BPF_REG_1, 0),
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800467 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700468 BPF_JMP_IMM(BPF_JEQ, BPF_REG_0, 0, 2),
469 BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 0),
470 BPF_EXIT_INSN(),
471 BPF_ST_MEM(BPF_DW, BPF_REG_0, 0, 1),
472 BPF_EXIT_INSN(),
473 },
474 .fixup = {3},
475 .errstr = "R0 invalid mem access",
476 .result = REJECT,
477 },
Alexei Starovoitovfd10c2e2014-09-29 18:50:02 -0700478 {
479 "jump test 1",
480 .insns = {
481 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
482 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_1, -8),
483 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 1),
484 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
485 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 1),
486 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 1),
487 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 1),
488 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 2),
489 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 1),
490 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 3),
491 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 1),
492 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 4),
493 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1),
494 BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 5),
495 BPF_MOV64_IMM(BPF_REG_0, 0),
496 BPF_EXIT_INSN(),
497 },
498 .result = ACCEPT,
499 },
500 {
501 "jump test 2",
502 .insns = {
503 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
504 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 2),
505 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
506 BPF_JMP_IMM(BPF_JA, 0, 0, 14),
507 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 2),
508 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0),
509 BPF_JMP_IMM(BPF_JA, 0, 0, 11),
510 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 2),
511 BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0),
512 BPF_JMP_IMM(BPF_JA, 0, 0, 8),
513 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 2),
514 BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0),
515 BPF_JMP_IMM(BPF_JA, 0, 0, 5),
516 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 2),
517 BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0),
518 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
519 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 1),
520 BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0),
521 BPF_MOV64_IMM(BPF_REG_0, 0),
522 BPF_EXIT_INSN(),
523 },
524 .result = ACCEPT,
525 },
526 {
527 "jump test 3",
528 .insns = {
529 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
530 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 0, 3),
531 BPF_ST_MEM(BPF_DW, BPF_REG_2, -8, 0),
532 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
533 BPF_JMP_IMM(BPF_JA, 0, 0, 19),
534 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 1, 3),
535 BPF_ST_MEM(BPF_DW, BPF_REG_2, -16, 0),
536 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -16),
537 BPF_JMP_IMM(BPF_JA, 0, 0, 15),
538 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 2, 3),
539 BPF_ST_MEM(BPF_DW, BPF_REG_2, -32, 0),
540 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -32),
541 BPF_JMP_IMM(BPF_JA, 0, 0, 11),
542 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 3, 3),
543 BPF_ST_MEM(BPF_DW, BPF_REG_2, -40, 0),
544 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -40),
545 BPF_JMP_IMM(BPF_JA, 0, 0, 7),
546 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 4, 3),
547 BPF_ST_MEM(BPF_DW, BPF_REG_2, -48, 0),
548 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -48),
549 BPF_JMP_IMM(BPF_JA, 0, 0, 3),
550 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, 5, 0),
551 BPF_ST_MEM(BPF_DW, BPF_REG_2, -56, 0),
552 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -56),
553 BPF_LD_MAP_FD(BPF_REG_1, 0),
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800554 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_delete_elem),
Alexei Starovoitovfd10c2e2014-09-29 18:50:02 -0700555 BPF_EXIT_INSN(),
556 },
557 .fixup = {24},
558 .result = ACCEPT,
559 },
560 {
561 "jump test 4",
562 .insns = {
563 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
564 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
565 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
566 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
567 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
568 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
569 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
570 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
571 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
572 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
573 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
574 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
575 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
576 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
577 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
578 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
579 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
580 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
581 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
582 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
583 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
584 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
585 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
586 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
587 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
588 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
589 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
590 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
591 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
592 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
593 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
594 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
595 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 1),
596 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 2),
597 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 3),
598 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 4),
599 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
600 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
601 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
602 BPF_JMP_IMM(BPF_JEQ, BPF_REG_1, BPF_REG_10, 0),
603 BPF_MOV64_IMM(BPF_REG_0, 0),
604 BPF_EXIT_INSN(),
605 },
606 .result = ACCEPT,
607 },
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700608 {
609 "jump test 5",
610 .insns = {
611 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
612 BPF_MOV64_REG(BPF_REG_3, BPF_REG_2),
613 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
614 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8),
615 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
616 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8),
617 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
618 BPF_MOV64_IMM(BPF_REG_0, 0),
619 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
620 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8),
621 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
622 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8),
623 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
624 BPF_MOV64_IMM(BPF_REG_0, 0),
625 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
626 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8),
627 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
628 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8),
629 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
630 BPF_MOV64_IMM(BPF_REG_0, 0),
631 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
632 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8),
633 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
634 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8),
635 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
636 BPF_MOV64_IMM(BPF_REG_0, 0),
637 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
638 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_3, -8),
639 BPF_JMP_IMM(BPF_JA, 0, 0, 2),
640 BPF_STX_MEM(BPF_DW, BPF_REG_2, BPF_REG_2, -8),
641 BPF_JMP_IMM(BPF_JA, 0, 0, 0),
642 BPF_MOV64_IMM(BPF_REG_0, 0),
643 BPF_EXIT_INSN(),
644 },
645 .result = ACCEPT,
646 },
Alexei Starovoitov614cd3b2015-03-13 11:57:43 -0700647 {
648 "access skb fields ok",
649 .insns = {
650 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
651 offsetof(struct __sk_buff, len)),
652 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
653 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
654 offsetof(struct __sk_buff, mark)),
655 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
656 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
657 offsetof(struct __sk_buff, pkt_type)),
658 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
659 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
660 offsetof(struct __sk_buff, queue_mapping)),
661 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
Alexei Starovoitovc2497392015-03-16 18:06:02 -0700662 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
663 offsetof(struct __sk_buff, protocol)),
664 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
665 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
666 offsetof(struct __sk_buff, vlan_present)),
667 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
668 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
669 offsetof(struct __sk_buff, vlan_tci)),
670 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 0),
Alexei Starovoitov614cd3b2015-03-13 11:57:43 -0700671 BPF_EXIT_INSN(),
672 },
673 .result = ACCEPT,
674 },
675 {
676 "access skb fields bad1",
677 .insns = {
678 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1, -4),
679 BPF_EXIT_INSN(),
680 },
681 .errstr = "invalid bpf_context access",
682 .result = REJECT,
683 },
684 {
685 "access skb fields bad2",
686 .insns = {
687 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 9),
688 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
689 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
690 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
691 BPF_LD_MAP_FD(BPF_REG_1, 0),
692 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
693 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
694 BPF_EXIT_INSN(),
695 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
696 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
697 offsetof(struct __sk_buff, pkt_type)),
698 BPF_EXIT_INSN(),
699 },
700 .fixup = {4},
701 .errstr = "different pointers",
702 .result = REJECT,
703 },
704 {
705 "access skb fields bad3",
706 .insns = {
707 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 2),
708 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
709 offsetof(struct __sk_buff, pkt_type)),
710 BPF_EXIT_INSN(),
711 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
712 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
713 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
714 BPF_LD_MAP_FD(BPF_REG_1, 0),
715 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
716 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
717 BPF_EXIT_INSN(),
718 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
719 BPF_JMP_IMM(BPF_JA, 0, 0, -12),
720 },
721 .fixup = {6},
722 .errstr = "different pointers",
723 .result = REJECT,
724 },
Alexei Starovoitov725f9dc2015-04-15 16:19:33 -0700725 {
726 "access skb fields bad4",
727 .insns = {
728 BPF_JMP_IMM(BPF_JGE, BPF_REG_1, 0, 3),
729 BPF_LDX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
730 offsetof(struct __sk_buff, len)),
731 BPF_MOV64_IMM(BPF_REG_0, 0),
732 BPF_EXIT_INSN(),
733 BPF_ST_MEM(BPF_DW, BPF_REG_10, -8, 0),
734 BPF_MOV64_REG(BPF_REG_2, BPF_REG_10),
735 BPF_ALU64_IMM(BPF_ADD, BPF_REG_2, -8),
736 BPF_LD_MAP_FD(BPF_REG_1, 0),
737 BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_map_lookup_elem),
738 BPF_JMP_IMM(BPF_JNE, BPF_REG_0, 0, 1),
739 BPF_EXIT_INSN(),
740 BPF_MOV64_REG(BPF_REG_1, BPF_REG_0),
741 BPF_JMP_IMM(BPF_JA, 0, 0, -13),
742 },
743 .fixup = {7},
744 .errstr = "different pointers",
745 .result = REJECT,
746 },
Alexei Starovoitovd691f9e2015-06-04 10:11:54 -0700747 {
748 "check skb->mark is not writeable by sockets",
749 .insns = {
750 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
751 offsetof(struct __sk_buff, mark)),
752 BPF_EXIT_INSN(),
753 },
754 .errstr = "invalid bpf_context access",
755 .result = REJECT,
756 },
757 {
758 "check skb->tc_index is not writeable by sockets",
759 .insns = {
760 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
761 offsetof(struct __sk_buff, tc_index)),
762 BPF_EXIT_INSN(),
763 },
764 .errstr = "invalid bpf_context access",
765 .result = REJECT,
766 },
767 {
768 "check non-u32 access to cb",
769 .insns = {
770 BPF_STX_MEM(BPF_H, BPF_REG_1, BPF_REG_1,
771 offsetof(struct __sk_buff, cb[0])),
772 BPF_EXIT_INSN(),
773 },
774 .errstr = "invalid bpf_context access",
775 .result = REJECT,
776 },
777 {
778 "check out of range skb->cb access",
779 .insns = {
780 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
781 offsetof(struct __sk_buff, cb[60])),
782 BPF_EXIT_INSN(),
783 },
784 .errstr = "invalid bpf_context access",
785 .result = REJECT,
786 .prog_type = BPF_PROG_TYPE_SCHED_ACT,
787 },
788 {
789 "write skb fields from socket prog",
790 .insns = {
791 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
792 offsetof(struct __sk_buff, cb[4])),
793 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
794 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
795 offsetof(struct __sk_buff, mark)),
796 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
797 offsetof(struct __sk_buff, tc_index)),
798 BPF_JMP_IMM(BPF_JGE, BPF_REG_0, 0, 1),
799 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
800 offsetof(struct __sk_buff, cb[0])),
801 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_1,
802 offsetof(struct __sk_buff, cb[2])),
803 BPF_EXIT_INSN(),
804 },
805 .result = ACCEPT,
806 },
807 {
808 "write skb fields from tc_cls_act prog",
809 .insns = {
810 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
811 offsetof(struct __sk_buff, cb[0])),
812 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
813 offsetof(struct __sk_buff, mark)),
814 BPF_LDX_MEM(BPF_W, BPF_REG_0, BPF_REG_1,
815 offsetof(struct __sk_buff, tc_index)),
816 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
817 offsetof(struct __sk_buff, tc_index)),
818 BPF_STX_MEM(BPF_W, BPF_REG_1, BPF_REG_0,
819 offsetof(struct __sk_buff, cb[3])),
820 BPF_EXIT_INSN(),
821 },
822 .result = ACCEPT,
823 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
824 },
Alex Gartrell24b4d2a2015-07-23 14:24:40 -0700825 {
826 "PTR_TO_STACK store/load",
827 .insns = {
828 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
829 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -10),
830 BPF_ST_MEM(BPF_DW, BPF_REG_1, 2, 0xfaceb00c),
831 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 2),
832 BPF_EXIT_INSN(),
833 },
834 .result = ACCEPT,
835 },
836 {
837 "PTR_TO_STACK store/load - bad alignment on off",
838 .insns = {
839 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
840 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
841 BPF_ST_MEM(BPF_DW, BPF_REG_1, 2, 0xfaceb00c),
842 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 2),
843 BPF_EXIT_INSN(),
844 },
845 .result = REJECT,
846 .errstr = "misaligned access off -6 size 8",
847 },
848 {
849 "PTR_TO_STACK store/load - bad alignment on reg",
850 .insns = {
851 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
852 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -10),
853 BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c),
854 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8),
855 BPF_EXIT_INSN(),
856 },
857 .result = REJECT,
858 .errstr = "misaligned access off -2 size 8",
859 },
860 {
861 "PTR_TO_STACK store/load - out of bounds low",
862 .insns = {
863 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
864 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -80000),
865 BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c),
866 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8),
867 BPF_EXIT_INSN(),
868 },
869 .result = REJECT,
870 .errstr = "invalid stack off=-79992 size=8",
871 },
872 {
873 "PTR_TO_STACK store/load - out of bounds high",
874 .insns = {
875 BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),
876 BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),
877 BPF_ST_MEM(BPF_DW, BPF_REG_1, 8, 0xfaceb00c),
878 BPF_LDX_MEM(BPF_DW, BPF_REG_0, BPF_REG_1, 8),
879 BPF_EXIT_INSN(),
880 },
881 .result = REJECT,
882 .errstr = "invalid stack off=0 size=8",
883 },
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700884};
885
886static int probe_filter_length(struct bpf_insn *fp)
887{
888 int len = 0;
889
890 for (len = MAX_INSNS - 1; len > 0; --len)
891 if (fp[len].code != 0 || fp[len].imm != 0)
892 break;
893
894 return len + 1;
895}
896
897static int create_map(void)
898{
899 long long key, value = 0;
900 int map_fd;
901
Alexei Starovoitov7943c0f2014-11-13 17:36:50 -0800902 map_fd = bpf_create_map(BPF_MAP_TYPE_HASH, sizeof(key), sizeof(value), 1024);
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700903 if (map_fd < 0) {
904 printf("failed to create map '%s'\n", strerror(errno));
905 }
906
907 return map_fd;
908}
909
910static int test(void)
911{
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700912 int prog_fd, i, pass_cnt = 0, err_cnt = 0;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700913
914 for (i = 0; i < ARRAY_SIZE(tests); i++) {
915 struct bpf_insn *prog = tests[i].insns;
Alexei Starovoitovd691f9e2015-06-04 10:11:54 -0700916 int prog_type = tests[i].prog_type;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700917 int prog_len = probe_filter_length(prog);
918 int *fixup = tests[i].fixup;
919 int map_fd = -1;
920
921 if (*fixup) {
922 map_fd = create_map();
923
924 do {
925 prog[*fixup].imm = map_fd;
926 fixup++;
927 } while (*fixup);
928 }
929 printf("#%d %s ", i, tests[i].descr);
930
Alexei Starovoitovd691f9e2015-06-04 10:11:54 -0700931 prog_fd = bpf_prog_load(prog_type ?: BPF_PROG_TYPE_SOCKET_FILTER,
932 prog, prog_len * sizeof(struct bpf_insn),
Alexei Starovoitovb896c4f2015-03-25 12:49:23 -0700933 "GPL", 0);
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700934
935 if (tests[i].result == ACCEPT) {
936 if (prog_fd < 0) {
937 printf("FAIL\nfailed to load prog '%s'\n",
938 strerror(errno));
939 printf("%s", bpf_log_buf);
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700940 err_cnt++;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700941 goto fail;
942 }
943 } else {
944 if (prog_fd >= 0) {
945 printf("FAIL\nunexpected success to load\n");
946 printf("%s", bpf_log_buf);
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700947 err_cnt++;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700948 goto fail;
949 }
950 if (strstr(bpf_log_buf, tests[i].errstr) == 0) {
951 printf("FAIL\nunexpected error message: %s",
952 bpf_log_buf);
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700953 err_cnt++;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700954 goto fail;
955 }
956 }
957
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700958 pass_cnt++;
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700959 printf("OK\n");
960fail:
961 if (map_fd >= 0)
962 close(map_fd);
963 close(prog_fd);
964
965 }
Alexei Starovoitov342ded42014-10-28 15:11:42 -0700966 printf("Summary: %d PASSED, %d FAILED\n", pass_cnt, err_cnt);
Alexei Starovoitov3c731eb2014-09-26 00:17:07 -0700967
968 return 0;
969}
970
971int main(void)
972{
973 return test();
974}