Loading...
Note: File does not exist in v6.13.7.
1#include <asm/types.h>
2#include <linux/types.h>
3#include <stdint.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <unistd.h>
7#include <errno.h>
8#include <string.h>
9#include <stddef.h>
10#include <stdbool.h>
11
12#include <linux/unistd.h>
13#include <linux/filter.h>
14#include <linux/bpf_perf_event.h>
15#include <linux/bpf.h>
16
17#include <bpf/bpf.h>
18
19#include "../../../include/linux/filter.h"
20#include "bpf_rlimit.h"
21
22#ifndef ARRAY_SIZE
23# define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
24#endif
25
26#define MAX_INSNS 512
27#define MAX_MATCHES 16
28
29struct bpf_reg_match {
30 unsigned int line;
31 const char *match;
32};
33
34struct bpf_align_test {
35 const char *descr;
36 struct bpf_insn insns[MAX_INSNS];
37 enum {
38 UNDEF,
39 ACCEPT,
40 REJECT
41 } result;
42 enum bpf_prog_type prog_type;
43 /* Matches must be in order of increasing line */
44 struct bpf_reg_match matches[MAX_MATCHES];
45};
46
47static struct bpf_align_test tests[] = {
48 /* Four tests of known constants. These aren't staggeringly
49 * interesting since we track exact values now.
50 */
51 {
52 .descr = "mov",
53 .insns = {
54 BPF_MOV64_IMM(BPF_REG_3, 2),
55 BPF_MOV64_IMM(BPF_REG_3, 4),
56 BPF_MOV64_IMM(BPF_REG_3, 8),
57 BPF_MOV64_IMM(BPF_REG_3, 16),
58 BPF_MOV64_IMM(BPF_REG_3, 32),
59 BPF_MOV64_IMM(BPF_REG_0, 0),
60 BPF_EXIT_INSN(),
61 },
62 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
63 .matches = {
64 {1, "R1=ctx(id=0,off=0,imm=0)"},
65 {1, "R10=fp0"},
66 {1, "R3_w=inv2"},
67 {2, "R3_w=inv4"},
68 {3, "R3_w=inv8"},
69 {4, "R3_w=inv16"},
70 {5, "R3_w=inv32"},
71 },
72 },
73 {
74 .descr = "shift",
75 .insns = {
76 BPF_MOV64_IMM(BPF_REG_3, 1),
77 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
78 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
79 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
80 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
81 BPF_ALU64_IMM(BPF_RSH, BPF_REG_3, 4),
82 BPF_MOV64_IMM(BPF_REG_4, 32),
83 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
84 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
85 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
86 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
87 BPF_MOV64_IMM(BPF_REG_0, 0),
88 BPF_EXIT_INSN(),
89 },
90 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
91 .matches = {
92 {1, "R1=ctx(id=0,off=0,imm=0)"},
93 {1, "R10=fp0"},
94 {1, "R3_w=inv1"},
95 {2, "R3_w=inv2"},
96 {3, "R3_w=inv4"},
97 {4, "R3_w=inv8"},
98 {5, "R3_w=inv16"},
99 {6, "R3_w=inv1"},
100 {7, "R4_w=inv32"},
101 {8, "R4_w=inv16"},
102 {9, "R4_w=inv8"},
103 {10, "R4_w=inv4"},
104 {11, "R4_w=inv2"},
105 },
106 },
107 {
108 .descr = "addsub",
109 .insns = {
110 BPF_MOV64_IMM(BPF_REG_3, 4),
111 BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 4),
112 BPF_ALU64_IMM(BPF_ADD, BPF_REG_3, 2),
113 BPF_MOV64_IMM(BPF_REG_4, 8),
114 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
115 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 2),
116 BPF_MOV64_IMM(BPF_REG_0, 0),
117 BPF_EXIT_INSN(),
118 },
119 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
120 .matches = {
121 {1, "R1=ctx(id=0,off=0,imm=0)"},
122 {1, "R10=fp0"},
123 {1, "R3_w=inv4"},
124 {2, "R3_w=inv8"},
125 {3, "R3_w=inv10"},
126 {4, "R4_w=inv8"},
127 {5, "R4_w=inv12"},
128 {6, "R4_w=inv14"},
129 },
130 },
131 {
132 .descr = "mul",
133 .insns = {
134 BPF_MOV64_IMM(BPF_REG_3, 7),
135 BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 1),
136 BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 2),
137 BPF_ALU64_IMM(BPF_MUL, BPF_REG_3, 4),
138 BPF_MOV64_IMM(BPF_REG_0, 0),
139 BPF_EXIT_INSN(),
140 },
141 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
142 .matches = {
143 {1, "R1=ctx(id=0,off=0,imm=0)"},
144 {1, "R10=fp0"},
145 {1, "R3_w=inv7"},
146 {2, "R3_w=inv7"},
147 {3, "R3_w=inv14"},
148 {4, "R3_w=inv56"},
149 },
150 },
151
152 /* Tests using unknown values */
153#define PREP_PKT_POINTERS \
154 BPF_LDX_MEM(BPF_W, BPF_REG_2, BPF_REG_1, \
155 offsetof(struct __sk_buff, data)), \
156 BPF_LDX_MEM(BPF_W, BPF_REG_3, BPF_REG_1, \
157 offsetof(struct __sk_buff, data_end))
158
159#define LOAD_UNKNOWN(DST_REG) \
160 PREP_PKT_POINTERS, \
161 BPF_MOV64_REG(BPF_REG_0, BPF_REG_2), \
162 BPF_ALU64_IMM(BPF_ADD, BPF_REG_0, 8), \
163 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_0, 1), \
164 BPF_EXIT_INSN(), \
165 BPF_LDX_MEM(BPF_B, DST_REG, BPF_REG_2, 0)
166
167 {
168 .descr = "unknown shift",
169 .insns = {
170 LOAD_UNKNOWN(BPF_REG_3),
171 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
172 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
173 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
174 BPF_ALU64_IMM(BPF_LSH, BPF_REG_3, 1),
175 LOAD_UNKNOWN(BPF_REG_4),
176 BPF_ALU64_IMM(BPF_LSH, BPF_REG_4, 5),
177 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
178 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
179 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
180 BPF_ALU64_IMM(BPF_RSH, BPF_REG_4, 1),
181 BPF_MOV64_IMM(BPF_REG_0, 0),
182 BPF_EXIT_INSN(),
183 },
184 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
185 .matches = {
186 {7, "R0=pkt(id=0,off=8,r=8,imm=0)"},
187 {7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
188 {8, "R3_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
189 {9, "R3_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
190 {10, "R3_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
191 {11, "R3_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
192 {18, "R3=pkt_end(id=0,off=0,imm=0)"},
193 {18, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
194 {19, "R4_w=inv(id=0,umax_value=8160,var_off=(0x0; 0x1fe0))"},
195 {20, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
196 {21, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
197 {22, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
198 {23, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
199 },
200 },
201 {
202 .descr = "unknown mul",
203 .insns = {
204 LOAD_UNKNOWN(BPF_REG_3),
205 BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
206 BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 1),
207 BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
208 BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
209 BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
210 BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 4),
211 BPF_MOV64_REG(BPF_REG_4, BPF_REG_3),
212 BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 8),
213 BPF_ALU64_IMM(BPF_MUL, BPF_REG_4, 2),
214 BPF_MOV64_IMM(BPF_REG_0, 0),
215 BPF_EXIT_INSN(),
216 },
217 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
218 .matches = {
219 {7, "R3_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
220 {8, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
221 {9, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
222 {10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
223 {11, "R4_w=inv(id=0,umax_value=510,var_off=(0x0; 0x1fe))"},
224 {12, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
225 {13, "R4_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
226 {14, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
227 {15, "R4_w=inv(id=0,umax_value=2040,var_off=(0x0; 0x7f8))"},
228 {16, "R4_w=inv(id=0,umax_value=4080,var_off=(0x0; 0xff0))"},
229 },
230 },
231 {
232 .descr = "packet const offset",
233 .insns = {
234 PREP_PKT_POINTERS,
235 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
236
237 BPF_MOV64_IMM(BPF_REG_0, 0),
238
239 /* Skip over ethernet header. */
240 BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
241 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
242 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
243 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
244 BPF_EXIT_INSN(),
245
246 BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 0),
247 BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 1),
248 BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 2),
249 BPF_LDX_MEM(BPF_B, BPF_REG_4, BPF_REG_5, 3),
250 BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 0),
251 BPF_LDX_MEM(BPF_H, BPF_REG_4, BPF_REG_5, 2),
252 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
253
254 BPF_MOV64_IMM(BPF_REG_0, 0),
255 BPF_EXIT_INSN(),
256 },
257 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
258 .matches = {
259 {4, "R5_w=pkt(id=0,off=0,r=0,imm=0)"},
260 {5, "R5_w=pkt(id=0,off=14,r=0,imm=0)"},
261 {6, "R4_w=pkt(id=0,off=14,r=0,imm=0)"},
262 {10, "R2=pkt(id=0,off=0,r=18,imm=0)"},
263 {10, "R5=pkt(id=0,off=14,r=18,imm=0)"},
264 {10, "R4_w=inv(id=0,umax_value=255,var_off=(0x0; 0xff))"},
265 {14, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},
266 {15, "R4_w=inv(id=0,umax_value=65535,var_off=(0x0; 0xffff))"},
267 },
268 },
269 {
270 .descr = "packet variable offset",
271 .insns = {
272 LOAD_UNKNOWN(BPF_REG_6),
273 BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
274
275 /* First, add a constant to the R5 packet pointer,
276 * then a variable with a known alignment.
277 */
278 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
279 BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
280 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
281 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
282 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
283 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
284 BPF_EXIT_INSN(),
285 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
286
287 /* Now, test in the other direction. Adding first
288 * the variable offset to R5, then the constant.
289 */
290 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
291 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
292 BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
293 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
294 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
295 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
296 BPF_EXIT_INSN(),
297 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
298
299 /* Test multiple accumulations of unknown values
300 * into a packet pointer.
301 */
302 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
303 BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
304 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
305 BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 4),
306 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
307 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
308 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
309 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
310 BPF_EXIT_INSN(),
311 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
312
313 BPF_MOV64_IMM(BPF_REG_0, 0),
314 BPF_EXIT_INSN(),
315 },
316 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
317 .matches = {
318 /* Calculated offset in R6 has unknown value, but known
319 * alignment of 4.
320 */
321 {8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
322 {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
323 /* Offset is added to packet pointer R5, resulting in
324 * known fixed offset, and variable offset from R6.
325 */
326 {11, "R5_w=pkt(id=1,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
327 /* At the time the word size load is performed from R5,
328 * it's total offset is NET_IP_ALIGN + reg->off (0) +
329 * reg->aux_off (14) which is 16. Then the variable
330 * offset is considered using reg->aux_off_align which
331 * is 4 and meets the load's requirements.
332 */
333 {15, "R4=pkt(id=1,off=18,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"},
334 {15, "R5=pkt(id=1,off=14,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"},
335 /* Variable offset is added to R5 packet pointer,
336 * resulting in auxiliary alignment of 4.
337 */
338 {18, "R5_w=pkt(id=2,off=0,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
339 /* Constant offset is added to R5, resulting in
340 * reg->off of 14.
341 */
342 {19, "R5_w=pkt(id=2,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
343 /* At the time the word size load is performed from R5,
344 * its total fixed offset is NET_IP_ALIGN + reg->off
345 * (14) which is 16. Then the variable offset is 4-byte
346 * aligned, so the total offset is 4-byte aligned and
347 * meets the load's requirements.
348 */
349 {23, "R4=pkt(id=2,off=18,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"},
350 {23, "R5=pkt(id=2,off=14,r=18,umax_value=1020,var_off=(0x0; 0x3fc))"},
351 /* Constant offset is added to R5 packet pointer,
352 * resulting in reg->off value of 14.
353 */
354 {26, "R5_w=pkt(id=0,off=14,r=8"},
355 /* Variable offset is added to R5, resulting in a
356 * variable offset of (4n).
357 */
358 {27, "R5_w=pkt(id=3,off=14,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
359 /* Constant is added to R5 again, setting reg->off to 18. */
360 {28, "R5_w=pkt(id=3,off=18,r=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
361 /* And once more we add a variable; resulting var_off
362 * is still (4n), fixed offset is not changed.
363 * Also, we create a new reg->id.
364 */
365 {29, "R5_w=pkt(id=4,off=18,r=0,umax_value=2040,var_off=(0x0; 0x7fc))"},
366 /* At the time the word size load is performed from R5,
367 * its total fixed offset is NET_IP_ALIGN + reg->off (18)
368 * which is 20. Then the variable offset is (4n), so
369 * the total offset is 4-byte aligned and meets the
370 * load's requirements.
371 */
372 {33, "R4=pkt(id=4,off=22,r=22,umax_value=2040,var_off=(0x0; 0x7fc))"},
373 {33, "R5=pkt(id=4,off=18,r=22,umax_value=2040,var_off=(0x0; 0x7fc))"},
374 },
375 },
376 {
377 .descr = "packet variable offset 2",
378 .insns = {
379 /* Create an unknown offset, (4n+2)-aligned */
380 LOAD_UNKNOWN(BPF_REG_6),
381 BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
382 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
383 /* Add it to the packet pointer */
384 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
385 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
386 /* Check bounds and perform a read */
387 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
388 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
389 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
390 BPF_EXIT_INSN(),
391 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
392 /* Make a (4n) offset from the value we just read */
393 BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xff),
394 BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
395 /* Add it to the packet pointer */
396 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
397 /* Check bounds and perform a read */
398 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
399 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
400 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
401 BPF_EXIT_INSN(),
402 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
403 BPF_MOV64_IMM(BPF_REG_0, 0),
404 BPF_EXIT_INSN(),
405 },
406 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
407 .matches = {
408 /* Calculated offset in R6 has unknown value, but known
409 * alignment of 4.
410 */
411 {8, "R2=pkt(id=0,off=0,r=8,imm=0)"},
412 {8, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
413 /* Adding 14 makes R6 be (4n+2) */
414 {9, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
415 /* Packet pointer has (4n+2) offset */
416 {11, "R5_w=pkt(id=1,off=0,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
417 {13, "R4=pkt(id=1,off=4,r=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
418 /* At the time the word size load is performed from R5,
419 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
420 * which is 2. Then the variable offset is (4n+2), so
421 * the total offset is 4-byte aligned and meets the
422 * load's requirements.
423 */
424 {15, "R5=pkt(id=1,off=0,r=4,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
425 /* Newly read value in R6 was shifted left by 2, so has
426 * known alignment of 4.
427 */
428 {18, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
429 /* Added (4n) to packet pointer's (4n+2) var_off, giving
430 * another (4n+2).
431 */
432 {19, "R5_w=pkt(id=2,off=0,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},
433 {21, "R4=pkt(id=2,off=4,r=0,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},
434 /* At the time the word size load is performed from R5,
435 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
436 * which is 2. Then the variable offset is (4n+2), so
437 * the total offset is 4-byte aligned and meets the
438 * load's requirements.
439 */
440 {23, "R5=pkt(id=2,off=0,r=4,umin_value=14,umax_value=2054,var_off=(0x2; 0xffc))"},
441 },
442 },
443 {
444 .descr = "dubious pointer arithmetic",
445 .insns = {
446 PREP_PKT_POINTERS,
447 BPF_MOV64_IMM(BPF_REG_0, 0),
448 /* (ptr - ptr) << 2 */
449 BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
450 BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),
451 BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),
452 /* We have a (4n) value. Let's make a packet offset
453 * out of it. First add 14, to make it a (4n+2)
454 */
455 BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
456 /* Then make sure it's nonnegative */
457 BPF_JMP_IMM(BPF_JSGE, BPF_REG_5, 0, 1),
458 BPF_EXIT_INSN(),
459 /* Add it to packet pointer */
460 BPF_MOV64_REG(BPF_REG_6, BPF_REG_2),
461 BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5),
462 /* Check bounds and perform a read */
463 BPF_MOV64_REG(BPF_REG_4, BPF_REG_6),
464 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
465 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
466 BPF_EXIT_INSN(),
467 BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_6, 0),
468 BPF_EXIT_INSN(),
469 },
470 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
471 .result = REJECT,
472 .matches = {
473 {4, "R5_w=pkt_end(id=0,off=0,imm=0)"},
474 /* (ptr - ptr) << 2 == unknown, (4n) */
475 {6, "R5_w=inv(id=0,smax_value=9223372036854775804,umax_value=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc))"},
476 /* (4n) + 14 == (4n+2). We blow our bounds, because
477 * the add could overflow.
478 */
479 {7, "R5=inv(id=0,var_off=(0x2; 0xfffffffffffffffc))"},
480 /* Checked s>=0 */
481 {9, "R5=inv(id=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
482 /* packet pointer + nonnegative (4n+2) */
483 {11, "R6_w=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
484 {13, "R4=pkt(id=1,off=4,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
485 /* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
486 * We checked the bounds, but it might have been able
487 * to overflow if the packet pointer started in the
488 * upper half of the address space.
489 * So we did not get a 'range' on R6, and the access
490 * attempt will fail.
491 */
492 {15, "R6=pkt(id=1,off=0,r=0,umin_value=2,umax_value=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc))"},
493 }
494 },
495 {
496 .descr = "variable subtraction",
497 .insns = {
498 /* Create an unknown offset, (4n+2)-aligned */
499 LOAD_UNKNOWN(BPF_REG_6),
500 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
501 BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
502 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
503 /* Create another unknown, (4n)-aligned, and subtract
504 * it from the first one
505 */
506 BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
507 BPF_ALU64_REG(BPF_SUB, BPF_REG_6, BPF_REG_7),
508 /* Bounds-check the result */
509 BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 1),
510 BPF_EXIT_INSN(),
511 /* Add it to the packet pointer */
512 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
513 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
514 /* Check bounds and perform a read */
515 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
516 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
517 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
518 BPF_EXIT_INSN(),
519 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
520 BPF_EXIT_INSN(),
521 },
522 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
523 .matches = {
524 /* Calculated offset in R6 has unknown value, but known
525 * alignment of 4.
526 */
527 {7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
528 {9, "R6_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
529 /* Adding 14 makes R6 be (4n+2) */
530 {10, "R6_w=inv(id=0,umin_value=14,umax_value=1034,var_off=(0x2; 0x7fc))"},
531 /* New unknown value in R7 is (4n) */
532 {11, "R7_w=inv(id=0,umax_value=1020,var_off=(0x0; 0x3fc))"},
533 /* Subtracting it from R6 blows our unsigned bounds */
534 {12, "R6=inv(id=0,smin_value=-1006,smax_value=1034,var_off=(0x2; 0xfffffffffffffffc))"},
535 /* Checked s>= 0 */
536 {14, "R6=inv(id=0,umin_value=2,umax_value=1034,var_off=(0x2; 0x7fc))"},
537 /* At the time the word size load is performed from R5,
538 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
539 * which is 2. Then the variable offset is (4n+2), so
540 * the total offset is 4-byte aligned and meets the
541 * load's requirements.
542 */
543 {20, "R5=pkt(id=1,off=0,r=4,umin_value=2,umax_value=1034,var_off=(0x2; 0x7fc))"},
544 },
545 },
546 {
547 .descr = "pointer variable subtraction",
548 .insns = {
549 /* Create an unknown offset, (4n+2)-aligned and bounded
550 * to [14,74]
551 */
552 LOAD_UNKNOWN(BPF_REG_6),
553 BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
554 BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xf),
555 BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
556 BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
557 /* Subtract it from the packet pointer */
558 BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
559 BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_6),
560 /* Create another unknown, (4n)-aligned and >= 74.
561 * That in fact means >= 76, since 74 % 4 == 2
562 */
563 BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
564 BPF_ALU64_IMM(BPF_ADD, BPF_REG_7, 76),
565 /* Add it to the packet pointer */
566 BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_7),
567 /* Check bounds and perform a read */
568 BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
569 BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
570 BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
571 BPF_EXIT_INSN(),
572 BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
573 BPF_EXIT_INSN(),
574 },
575 .prog_type = BPF_PROG_TYPE_SCHED_CLS,
576 .matches = {
577 /* Calculated offset in R6 has unknown value, but known
578 * alignment of 4.
579 */
580 {7, "R2=pkt(id=0,off=0,r=8,imm=0)"},
581 {10, "R6_w=inv(id=0,umax_value=60,var_off=(0x0; 0x3c))"},
582 /* Adding 14 makes R6 be (4n+2) */
583 {11, "R6_w=inv(id=0,umin_value=14,umax_value=74,var_off=(0x2; 0x7c))"},
584 /* Subtracting from packet pointer overflows ubounds */
585 {13, "R5_w=pkt(id=1,off=0,r=8,umin_value=18446744073709551542,umax_value=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c))"},
586 /* New unknown value in R7 is (4n), >= 76 */
587 {15, "R7_w=inv(id=0,umin_value=76,umax_value=1096,var_off=(0x0; 0x7fc))"},
588 /* Adding it to packet pointer gives nice bounds again */
589 {16, "R5_w=pkt(id=2,off=0,r=0,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"},
590 /* At the time the word size load is performed from R5,
591 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
592 * which is 2. Then the variable offset is (4n+2), so
593 * the total offset is 4-byte aligned and meets the
594 * load's requirements.
595 */
596 {20, "R5=pkt(id=2,off=0,r=4,umin_value=2,umax_value=1082,var_off=(0x2; 0x7fc))"},
597 },
598 },
599};
600
601static int probe_filter_length(const struct bpf_insn *fp)
602{
603 int len;
604
605 for (len = MAX_INSNS - 1; len > 0; --len)
606 if (fp[len].code != 0 || fp[len].imm != 0)
607 break;
608 return len + 1;
609}
610
611static char bpf_vlog[32768];
612
613static int do_test_single(struct bpf_align_test *test)
614{
615 struct bpf_insn *prog = test->insns;
616 int prog_type = test->prog_type;
617 char bpf_vlog_copy[32768];
618 const char *line_ptr;
619 int cur_line = -1;
620 int prog_len, i;
621 int fd_prog;
622 int ret;
623
624 prog_len = probe_filter_length(prog);
625 fd_prog = bpf_verify_program(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER,
626 prog, prog_len, 1, "GPL", 0,
627 bpf_vlog, sizeof(bpf_vlog), 2);
628 if (fd_prog < 0 && test->result != REJECT) {
629 printf("Failed to load program.\n");
630 printf("%s", bpf_vlog);
631 ret = 1;
632 } else if (fd_prog >= 0 && test->result == REJECT) {
633 printf("Unexpected success to load!\n");
634 printf("%s", bpf_vlog);
635 ret = 1;
636 close(fd_prog);
637 } else {
638 ret = 0;
639 /* We make a local copy so that we can strtok() it */
640 strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
641 line_ptr = strtok(bpf_vlog_copy, "\n");
642 for (i = 0; i < MAX_MATCHES; i++) {
643 struct bpf_reg_match m = test->matches[i];
644
645 if (!m.match)
646 break;
647 while (line_ptr) {
648 cur_line = -1;
649 sscanf(line_ptr, "%u: ", &cur_line);
650 if (cur_line == m.line)
651 break;
652 line_ptr = strtok(NULL, "\n");
653 }
654 if (!line_ptr) {
655 printf("Failed to find line %u for match: %s\n",
656 m.line, m.match);
657 ret = 1;
658 printf("%s", bpf_vlog);
659 break;
660 }
661 if (!strstr(line_ptr, m.match)) {
662 printf("Failed to find match %u: %s\n",
663 m.line, m.match);
664 ret = 1;
665 printf("%s", bpf_vlog);
666 break;
667 }
668 }
669 if (fd_prog >= 0)
670 close(fd_prog);
671 }
672 return ret;
673}
674
675static int do_test(unsigned int from, unsigned int to)
676{
677 int all_pass = 0;
678 int all_fail = 0;
679 unsigned int i;
680
681 for (i = from; i < to; i++) {
682 struct bpf_align_test *test = &tests[i];
683 int fail;
684
685 printf("Test %3d: %s ... ",
686 i, test->descr);
687 fail = do_test_single(test);
688 if (fail) {
689 all_fail++;
690 printf("FAIL\n");
691 } else {
692 all_pass++;
693 printf("PASS\n");
694 }
695 }
696 printf("Results: %d pass %d fail\n",
697 all_pass, all_fail);
698 return all_fail ? EXIT_FAILURE : EXIT_SUCCESS;
699}
700
701int main(int argc, char **argv)
702{
703 unsigned int from = 0, to = ARRAY_SIZE(tests);
704
705 if (argc == 3) {
706 unsigned int l = atoi(argv[argc - 2]);
707 unsigned int u = atoi(argv[argc - 1]);
708
709 if (l < to && u < to) {
710 from = l;
711 to = u + 1;
712 }
713 } else if (argc == 2) {
714 unsigned int t = atoi(argv[argc - 1]);
715
716 if (t < to) {
717 from = t;
718 to = t + 1;
719 }
720 }
721 return do_test(from, to);
722}