1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3
4 #define MAX_INSNS 512
5 #define MAX_MATCHES 16
6
7 struct bpf_reg_match {
8 unsigned int line;
9 const char *match;
10 };
11
12 struct 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
25 static 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 {0, "R1=ctx(off=0,imm=0)"},
43 {0, "R10=fp0"},
44 {0, "R3_w=2"},
45 {1, "R3_w=4"},
46 {2, "R3_w=8"},
47 {3, "R3_w=16"},
48 {4, "R3_w=32"},
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 {0, "R1=ctx(off=0,imm=0)"},
71 {0, "R10=fp0"},
72 {0, "R3_w=1"},
73 {1, "R3_w=2"},
74 {2, "R3_w=4"},
75 {3, "R3_w=8"},
76 {4, "R3_w=16"},
77 {5, "R3_w=1"},
78 {6, "R4_w=32"},
79 {7, "R4_w=16"},
80 {8, "R4_w=8"},
81 {9, "R4_w=4"},
82 {10, "R4_w=2"},
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 {0, "R1=ctx(off=0,imm=0)"},
100 {0, "R10=fp0"},
101 {0, "R3_w=4"},
102 {1, "R3_w=8"},
103 {2, "R3_w=10"},
104 {3, "R4_w=8"},
105 {4, "R4_w=12"},
106 {5, "R4_w=14"},
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 {0, "R1=ctx(off=0,imm=0)"},
122 {0, "R10=fp0"},
123 {0, "R3_w=7"},
124 {1, "R3_w=7"},
125 {2, "R3_w=14"},
126 {3, "R3_w=56"},
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 {6, "R0_w=pkt(off=8,r=8,imm=0)"},
165 {6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
166 {7, "R3_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
167 {8, "R3_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
168 {9, "R3_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
169 {10, "R3_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
170 {12, "R3_w=pkt_end(off=0,imm=0)"},
171 {17, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
172 {18, "R4_w=scalar(umax=8160,var_off=(0x0; 0x1fe0))"},
173 {19, "R4_w=scalar(umax=4080,var_off=(0x0; 0xff0))"},
174 {20, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
175 {21, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
176 {22, "R4_w=scalar(umax=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 {6, "R3_w=scalar(umax=255,var_off=(0x0; 0xff))"},
198 {7, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
199 {8, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
200 {9, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
201 {10, "R4_w=scalar(umax=510,var_off=(0x0; 0x1fe))"},
202 {11, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
203 {12, "R4_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
204 {13, "R4_w=scalar(id=1,umax=255,var_off=(0x0; 0xff))"},
205 {14, "R4_w=scalar(umax=2040,var_off=(0x0; 0x7f8))"},
206 {15, "R4_w=scalar(umax=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 {2, "R5_w=pkt(off=0,r=0,imm=0)"},
238 {4, "R5_w=pkt(off=14,r=0,imm=0)"},
239 {5, "R4_w=pkt(off=14,r=0,imm=0)"},
240 {9, "R2=pkt(off=0,r=18,imm=0)"},
241 {10, "R5=pkt(off=14,r=18,imm=0)"},
242 {10, "R4_w=scalar(umax=255,var_off=(0x0; 0xff))"},
243 {13, "R4_w=scalar(umax=65535,var_off=(0x0; 0xffff))"},
244 {14, "R4_w=scalar(umax=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 {6, "R2_w=pkt(off=0,r=8,imm=0)"},
300 {7, "R6_w=scalar(umax=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=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=1020,var_off=(0x0; 0x3fc))"},
312 {15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
313 /* Variable offset is added to R5 packet pointer,
314 * resulting in auxiliary alignment of 4.
315 */
316 {17, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
317 /* Constant offset is added to R5, resulting in
318 * reg->off of 14.
319 */
320 {18, "R5_w=pkt(id=2,off=14,r=0,umax=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=1020,var_off=(0x0; 0x3fc))"},
328 {23, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
329 /* Constant offset is added to R5 packet pointer,
330 * resulting in reg->off value of 14.
331 */
332 {25, "R5_w=pkt(off=14,r=8"},
333 /* Variable offset is added to R5, resulting in a
334 * variable offset of (4n).
335 */
336 {26, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
337 /* Constant is added to R5 again, setting reg->off to 18. */
338 {27, "R5_w=pkt(id=3,off=18,r=0,umax=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 {28, "R5_w=pkt(id=4,off=18,r=0,umax=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=2040,var_off=(0x0; 0x7fc)"},
351 {33, "R5=pkt(id=4,off=18,r=22,umax=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 {6, "R2_w=pkt(off=0,r=8,imm=0)"},
390 {7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
391 /* Adding 14 makes R6 be (4n+2) */
392 {8, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
393 /* Packet pointer has (4n+2) offset */
394 {11, "R5_w=pkt(id=1,off=0,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
395 {12, "R4=pkt(id=1,off=4,r=0,umin=14,umax=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=14,umax=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 {17, "R6_w=scalar(umax=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=14,umax=2054,var_off=(0x2; 0xffc)"},
411 {20, "R4=pkt(id=2,off=4,r=0,umin=14,umax=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=14,umax=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 {3, "R5_w=pkt_end(off=0,imm=0)"},
452 /* (ptr - ptr) << 2 == unknown, (4n) */
453 {5, "R5_w=scalar(smax=9223372036854775804,umax=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"},
454 /* (4n) + 14 == (4n+2). We blow our bounds, because
455 * the add could overflow.
456 */
457 {6, "R5_w=scalar(smin=-9223372036854775806,smax=9223372036854775806,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
458 /* Checked s>=0 */
459 {9, "R5=scalar(umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
460 /* packet pointer + nonnegative (4n+2) */
461 {11, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
462 {12, "R4_w=pkt(id=1,off=4,r=0,umin=2,umax=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=2,umax=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 {6, "R2_w=pkt(off=0,r=8,imm=0)"},
506 {8, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
507 /* Adding 14 makes R6 be (4n+2) */
508 {9, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
509 /* New unknown value in R7 is (4n) */
510 {10, "R7_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
511 /* Subtracting it from R6 blows our unsigned bounds */
512 {11, "R6=scalar(smin=-1006,smax=1034,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
513 /* Checked s>= 0 */
514 {14, "R6=scalar(umin=2,umax=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=2,umax=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 {6, "R2_w=pkt(off=0,r=8,imm=0)"},
560 {9, "R6_w=scalar(umax=60,var_off=(0x0; 0x3c))"},
561 /* Adding 14 makes R6 be (4n+2) */
562 {10, "R6_w=scalar(umin=14,umax=74,var_off=(0x2; 0x7c))"},
563 /* Subtracting from packet pointer overflows ubounds */
564 {13, "R5_w=pkt(id=2,off=0,r=8,umin=18446744073709551542,umax=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"},
565 /* New unknown value in R7 is (4n), >= 76 */
566 {14, "R7_w=scalar(umin=76,umax=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=2,umax=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=2,umax=1082,var_off=(0x2; 0xfffffffc)"},
576 },
577 },
578 };
579
probe_filter_length(const struct bpf_insn * fp)580 static 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
590 static char bpf_vlog[32768];
591
do_test_single(struct bpf_align_test * test)592 static 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 LIBBPF_OPTS(bpf_prog_load_opts, opts,
598 .prog_flags = BPF_F_STRICT_ALIGNMENT,
599 .log_buf = bpf_vlog,
600 .log_size = sizeof(bpf_vlog),
601 .log_level = 2,
602 );
603 const char *line_ptr;
604 int cur_line = -1;
605 int prog_len, i;
606 int fd_prog;
607 int ret;
608
609 prog_len = probe_filter_length(prog);
610 fd_prog = bpf_prog_load(prog_type ? : BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL",
611 prog, prog_len, &opts);
612 if (fd_prog < 0 && test->result != REJECT) {
613 printf("Failed to load program.\n");
614 printf("%s", bpf_vlog);
615 ret = 1;
616 } else if (fd_prog >= 0 && test->result == REJECT) {
617 printf("Unexpected success to load!\n");
618 printf("%s", bpf_vlog);
619 ret = 1;
620 close(fd_prog);
621 } else {
622 ret = 0;
623 /* We make a local copy so that we can strtok() it */
624 strncpy(bpf_vlog_copy, bpf_vlog, sizeof(bpf_vlog_copy));
625 line_ptr = strtok(bpf_vlog_copy, "\n");
626 for (i = 0; i < MAX_MATCHES; i++) {
627 struct bpf_reg_match m = test->matches[i];
628 int tmp;
629
630 if (!m.match)
631 break;
632 while (line_ptr) {
633 cur_line = -1;
634 sscanf(line_ptr, "%u: ", &cur_line);
635 if (cur_line == -1)
636 sscanf(line_ptr, "from %u to %u: ", &tmp, &cur_line);
637 if (cur_line == m.line)
638 break;
639 line_ptr = strtok(NULL, "\n");
640 }
641 if (!line_ptr) {
642 printf("Failed to find line %u for match: %s\n",
643 m.line, m.match);
644 ret = 1;
645 printf("%s", bpf_vlog);
646 break;
647 }
648 /* Check the next line as well in case the previous line
649 * did not have a corresponding bpf insn. Example:
650 * func#0 @0
651 * 0: R1=ctx(off=0,imm=0) R10=fp0
652 * 0: (b7) r3 = 2 ; R3_w=2
653 */
654 if (!strstr(line_ptr, m.match)) {
655 cur_line = -1;
656 line_ptr = strtok(NULL, "\n");
657 sscanf(line_ptr, "%u: ", &cur_line);
658 }
659 if (cur_line != m.line || !line_ptr ||
660 !strstr(line_ptr, m.match)) {
661 printf("Failed to find match %u: %s\n",
662 m.line, m.match);
663 ret = 1;
664 printf("%s", bpf_vlog);
665 break;
666 }
667 }
668 if (fd_prog >= 0)
669 close(fd_prog);
670 }
671 return ret;
672 }
673
test_align(void)674 void test_align(void)
675 {
676 unsigned int i;
677
678 for (i = 0; i < ARRAY_SIZE(tests); i++) {
679 struct bpf_align_test *test = &tests[i];
680
681 if (!test__start_subtest(test->descr))
682 continue;
683
684 CHECK_FAIL(do_test_single(test));
685 }
686 }
687