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