1 // SPDX-License-Identifier: GPL-2.0
2 #include <test_progs.h>
3 
4 #define MAX_INSNS	512
5 #define MAX_MATCHES	24
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_MOV64_REG(BPF_REG_4, BPF_REG_5),
271 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
272 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
273 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
274 			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
275 			BPF_EXIT_INSN(),
276 			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
277 
278 			/* Test multiple accumulations of unknown values
279 			 * into a packet pointer.
280 			 */
281 			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
282 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
283 			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
284 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
285 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 4),
286 			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
287 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
288 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
289 			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
290 			BPF_EXIT_INSN(),
291 			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_5, 0),
292 
293 			BPF_MOV64_IMM(BPF_REG_0, 0),
294 			BPF_EXIT_INSN(),
295 		},
296 		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
297 		.matches = {
298 			/* Calculated offset in R6 has unknown value, but known
299 			 * alignment of 4.
300 			 */
301 			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
302 			{7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
303 			/* Offset is added to packet pointer R5, resulting in
304 			 * known fixed offset, and variable offset from R6.
305 			 */
306 			{11, "R5_w=pkt(id=1,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
307 			/* At the time the word size load is performed from R5,
308 			 * it's total offset is NET_IP_ALIGN + reg->off (0) +
309 			 * reg->aux_off (14) which is 16.  Then the variable
310 			 * offset is considered using reg->aux_off_align which
311 			 * is 4 and meets the load's requirements.
312 			 */
313 			{15, "R4=pkt(id=1,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
314 			{15, "R5=pkt(id=1,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
315 			/* Variable offset is added to R5 packet pointer,
316 			 * resulting in auxiliary alignment of 4. To avoid BPF
317 			 * verifier's precision backtracking logging
318 			 * interfering we also have a no-op R4 = R5
319 			 * instruction to validate R5 state. We also check
320 			 * that R4 is what it should be in such case.
321 			 */
322 			{18, "R4_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
323 			{18, "R5_w=pkt(id=2,off=0,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
324 			/* Constant offset is added to R5, resulting in
325 			 * reg->off of 14.
326 			 */
327 			{19, "R5_w=pkt(id=2,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
328 			/* At the time the word size load is performed from R5,
329 			 * its total fixed offset is NET_IP_ALIGN + reg->off
330 			 * (14) which is 16.  Then the variable offset is 4-byte
331 			 * aligned, so the total offset is 4-byte aligned and
332 			 * meets the load's requirements.
333 			 */
334 			{24, "R4=pkt(id=2,off=18,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
335 			{24, "R5=pkt(id=2,off=14,r=18,umax=1020,var_off=(0x0; 0x3fc))"},
336 			/* Constant offset is added to R5 packet pointer,
337 			 * resulting in reg->off value of 14.
338 			 */
339 			{26, "R5_w=pkt(off=14,r=8"},
340 			/* Variable offset is added to R5, resulting in a
341 			 * variable offset of (4n). See comment for insn #18
342 			 * for R4 = R5 trick.
343 			 */
344 			{28, "R4_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
345 			{28, "R5_w=pkt(id=3,off=14,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
346 			/* Constant is added to R5 again, setting reg->off to 18. */
347 			{29, "R5_w=pkt(id=3,off=18,r=0,umax=1020,var_off=(0x0; 0x3fc))"},
348 			/* And once more we add a variable; resulting var_off
349 			 * is still (4n), fixed offset is not changed.
350 			 * Also, we create a new reg->id.
351 			 */
352 			{31, "R4_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"},
353 			{31, "R5_w=pkt(id=4,off=18,r=0,umax=2040,var_off=(0x0; 0x7fc)"},
354 			/* At the time the word size load is performed from R5,
355 			 * its total fixed offset is NET_IP_ALIGN + reg->off (18)
356 			 * which is 20.  Then the variable offset is (4n), so
357 			 * the total offset is 4-byte aligned and meets the
358 			 * load's requirements.
359 			 */
360 			{35, "R4=pkt(id=4,off=22,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
361 			{35, "R5=pkt(id=4,off=18,r=22,umax=2040,var_off=(0x0; 0x7fc)"},
362 		},
363 	},
364 	{
365 		.descr = "packet variable offset 2",
366 		.insns = {
367 			/* Create an unknown offset, (4n+2)-aligned */
368 			LOAD_UNKNOWN(BPF_REG_6),
369 			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
370 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
371 			/* Add it to the packet pointer */
372 			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
373 			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
374 			/* Check bounds and perform a read */
375 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
376 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
377 			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
378 			BPF_EXIT_INSN(),
379 			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
380 			/* Make a (4n) offset from the value we just read */
381 			BPF_ALU64_IMM(BPF_AND, BPF_REG_6, 0xff),
382 			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
383 			/* Add it to the packet pointer */
384 			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
385 			/* Check bounds and perform a read */
386 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
387 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
388 			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
389 			BPF_EXIT_INSN(),
390 			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
391 			BPF_MOV64_IMM(BPF_REG_0, 0),
392 			BPF_EXIT_INSN(),
393 		},
394 		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
395 		.matches = {
396 			/* Calculated offset in R6 has unknown value, but known
397 			 * alignment of 4.
398 			 */
399 			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
400 			{7, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
401 			/* Adding 14 makes R6 be (4n+2) */
402 			{8, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
403 			/* Packet pointer has (4n+2) offset */
404 			{11, "R5_w=pkt(id=1,off=0,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
405 			{12, "R4=pkt(id=1,off=4,r=0,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
406 			/* At the time the word size load is performed from R5,
407 			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
408 			 * which is 2.  Then the variable offset is (4n+2), so
409 			 * the total offset is 4-byte aligned and meets the
410 			 * load's requirements.
411 			 */
412 			{15, "R5=pkt(id=1,off=0,r=4,umin=14,umax=1034,var_off=(0x2; 0x7fc)"},
413 			/* Newly read value in R6 was shifted left by 2, so has
414 			 * known alignment of 4.
415 			 */
416 			{17, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
417 			/* Added (4n) to packet pointer's (4n+2) var_off, giving
418 			 * another (4n+2).
419 			 */
420 			{19, "R5_w=pkt(id=2,off=0,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
421 			{20, "R4=pkt(id=2,off=4,r=0,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
422 			/* At the time the word size load is performed from R5,
423 			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
424 			 * which is 2.  Then the variable offset is (4n+2), so
425 			 * the total offset is 4-byte aligned and meets the
426 			 * load's requirements.
427 			 */
428 			{23, "R5=pkt(id=2,off=0,r=4,umin=14,umax=2054,var_off=(0x2; 0xffc)"},
429 		},
430 	},
431 	{
432 		.descr = "dubious pointer arithmetic",
433 		.insns = {
434 			PREP_PKT_POINTERS,
435 			BPF_MOV64_IMM(BPF_REG_0, 0),
436 			/* (ptr - ptr) << 2 */
437 			BPF_MOV64_REG(BPF_REG_5, BPF_REG_3),
438 			BPF_ALU64_REG(BPF_SUB, BPF_REG_5, BPF_REG_2),
439 			BPF_ALU64_IMM(BPF_LSH, BPF_REG_5, 2),
440 			/* We have a (4n) value.  Let's make a packet offset
441 			 * out of it.  First add 14, to make it a (4n+2)
442 			 */
443 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_5, 14),
444 			/* Then make sure it's nonnegative */
445 			BPF_JMP_IMM(BPF_JSGE, BPF_REG_5, 0, 1),
446 			BPF_EXIT_INSN(),
447 			/* Add it to packet pointer */
448 			BPF_MOV64_REG(BPF_REG_6, BPF_REG_2),
449 			BPF_ALU64_REG(BPF_ADD, BPF_REG_6, BPF_REG_5),
450 			/* Check bounds and perform a read */
451 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_6),
452 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
453 			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
454 			BPF_EXIT_INSN(),
455 			BPF_LDX_MEM(BPF_W, BPF_REG_4, BPF_REG_6, 0),
456 			BPF_EXIT_INSN(),
457 		},
458 		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
459 		.result = REJECT,
460 		.matches = {
461 			{3, "R5_w=pkt_end(off=0,imm=0)"},
462 			/* (ptr - ptr) << 2 == unknown, (4n) */
463 			{5, "R5_w=scalar(smax=9223372036854775804,umax=18446744073709551612,var_off=(0x0; 0xfffffffffffffffc)"},
464 			/* (4n) + 14 == (4n+2).  We blow our bounds, because
465 			 * the add could overflow.
466 			 */
467 			{6, "R5_w=scalar(smin=-9223372036854775806,smax=9223372036854775806,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
468 			/* Checked s>=0 */
469 			{9, "R5=scalar(umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
470 			/* packet pointer + nonnegative (4n+2) */
471 			{11, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
472 			{12, "R4_w=pkt(id=1,off=4,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
473 			/* NET_IP_ALIGN + (4n+2) == (4n), alignment is fine.
474 			 * We checked the bounds, but it might have been able
475 			 * to overflow if the packet pointer started in the
476 			 * upper half of the address space.
477 			 * So we did not get a 'range' on R6, and the access
478 			 * attempt will fail.
479 			 */
480 			{15, "R6_w=pkt(id=1,off=0,r=0,umin=2,umax=9223372036854775806,var_off=(0x2; 0x7ffffffffffffffc)"},
481 		}
482 	},
483 	{
484 		.descr = "variable subtraction",
485 		.insns = {
486 			/* Create an unknown offset, (4n+2)-aligned */
487 			LOAD_UNKNOWN(BPF_REG_6),
488 			BPF_MOV64_REG(BPF_REG_7, BPF_REG_6),
489 			BPF_ALU64_IMM(BPF_LSH, BPF_REG_6, 2),
490 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_6, 14),
491 			/* Create another unknown, (4n)-aligned, and subtract
492 			 * it from the first one
493 			 */
494 			BPF_ALU64_IMM(BPF_LSH, BPF_REG_7, 2),
495 			BPF_ALU64_REG(BPF_SUB, BPF_REG_6, BPF_REG_7),
496 			/* Bounds-check the result */
497 			BPF_JMP_IMM(BPF_JSGE, BPF_REG_6, 0, 1),
498 			BPF_EXIT_INSN(),
499 			/* Add it to the packet pointer */
500 			BPF_MOV64_REG(BPF_REG_5, BPF_REG_2),
501 			BPF_ALU64_REG(BPF_ADD, BPF_REG_5, BPF_REG_6),
502 			/* Check bounds and perform a read */
503 			BPF_MOV64_REG(BPF_REG_4, BPF_REG_5),
504 			BPF_ALU64_IMM(BPF_ADD, BPF_REG_4, 4),
505 			BPF_JMP_REG(BPF_JGE, BPF_REG_3, BPF_REG_4, 1),
506 			BPF_EXIT_INSN(),
507 			BPF_LDX_MEM(BPF_W, BPF_REG_6, BPF_REG_5, 0),
508 			BPF_EXIT_INSN(),
509 		},
510 		.prog_type = BPF_PROG_TYPE_SCHED_CLS,
511 		.matches = {
512 			/* Calculated offset in R6 has unknown value, but known
513 			 * alignment of 4.
514 			 */
515 			{6, "R2_w=pkt(off=0,r=8,imm=0)"},
516 			{8, "R6_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
517 			/* Adding 14 makes R6 be (4n+2) */
518 			{9, "R6_w=scalar(umin=14,umax=1034,var_off=(0x2; 0x7fc))"},
519 			/* New unknown value in R7 is (4n) */
520 			{10, "R7_w=scalar(umax=1020,var_off=(0x0; 0x3fc))"},
521 			/* Subtracting it from R6 blows our unsigned bounds */
522 			{11, "R6=scalar(smin=-1006,smax=1034,umin=2,umax=18446744073709551614,var_off=(0x2; 0xfffffffffffffffc)"},
523 			/* Checked s>= 0 */
524 			{14, "R6=scalar(umin=2,umax=1034,var_off=(0x2; 0x7fc))"},
525 			/* At the time the word size load is performed from R5,
526 			 * its total fixed offset is NET_IP_ALIGN + reg->off (0)
527 			 * which is 2.  Then the variable offset is (4n+2), so
528 			 * the total offset is 4-byte aligned and meets the
529 			 * load's requirements.
530 			 */
531 			{20, "R5=pkt(id=2,off=0,r=4,umin=2,umax=1034,var_off=(0x2; 0x7fc)"},
532 
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(off=0,r=8,imm=0)"},
570 			{9, "R6_w=scalar(umax=60,var_off=(0x0; 0x3c))"},
571 			/* Adding 14 makes R6 be (4n+2) */
572 			{10, "R6_w=scalar(umin=14,umax=74,var_off=(0x2; 0x7c))"},
573 			/* Subtracting from packet pointer overflows ubounds */
574 			{13, "R5_w=pkt(id=2,off=0,r=8,umin=18446744073709551542,umax=18446744073709551602,var_off=(0xffffffffffffff82; 0x7c)"},
575 			/* New unknown value in R7 is (4n), >= 76 */
576 			{14, "R7_w=scalar(umin=76,umax=1096,var_off=(0x0; 0x7fc))"},
577 			/* Adding it to packet pointer gives nice bounds again */
578 			{16, "R5_w=pkt(id=3,off=0,r=0,umin=2,umax=1082,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=pkt(id=3,off=0,r=4,umin=2,umax=1082,var_off=(0x2; 0x7fc)"},
586 		},
587 	},
588 };
589 
590 static 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 
600 static char bpf_vlog[32768];
601 
602 static 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 			int tmp;
639 
640 			if (!m.match)
641 				break;
642 			while (line_ptr) {
643 				cur_line = -1;
644 				sscanf(line_ptr, "%u: ", &cur_line);
645 				if (cur_line == -1)
646 					sscanf(line_ptr, "from %u to %u: ", &tmp, &cur_line);
647 				if (cur_line == m.line)
648 					break;
649 				line_ptr = strtok(NULL, "\n");
650 			}
651 			if (!line_ptr) {
652 				printf("Failed to find line %u for match: %s\n",
653 				       m.line, m.match);
654 				ret = 1;
655 				printf("%s", bpf_vlog);
656 				break;
657 			}
658 			/* Check the next line as well in case the previous line
659 			 * did not have a corresponding bpf insn. Example:
660 			 * func#0 @0
661 			 * 0: R1=ctx(off=0,imm=0) R10=fp0
662 			 * 0: (b7) r3 = 2                 ; R3_w=2
663 			 *
664 			 * Sometimes it's actually two lines below, e.g. when
665 			 * searching for "6: R3_w=scalar(umax=255,var_off=(0x0; 0xff))":
666 			 *   from 4 to 6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0
667 			 *   6: R0_w=pkt(off=8,r=8,imm=0) R1=ctx(off=0,imm=0) R2_w=pkt(off=0,r=8,imm=0) R3_w=pkt_end(off=0,imm=0) R10=fp0
668 			 *   6: (71) r3 = *(u8 *)(r2 +0)           ; R2_w=pkt(off=0,r=8,imm=0) R3_w=scalar(umax=255,var_off=(0x0; 0xff))
669 			 */
670 			while (!strstr(line_ptr, m.match)) {
671 				cur_line = -1;
672 				line_ptr = strtok(NULL, "\n");
673 				sscanf(line_ptr ?: "", "%u: ", &cur_line);
674 				if (!line_ptr || cur_line != m.line)
675 					break;
676 			}
677 			if (cur_line != m.line || !line_ptr || !strstr(line_ptr, m.match)) {
678 				printf("Failed to find match %u: %s\n", m.line, m.match);
679 				ret = 1;
680 				printf("%s", bpf_vlog);
681 				break;
682 			}
683 		}
684 		if (fd_prog >= 0)
685 			close(fd_prog);
686 	}
687 	return ret;
688 }
689 
690 void test_align(void)
691 {
692 	unsigned int i;
693 
694 	for (i = 0; i < ARRAY_SIZE(tests); i++) {
695 		struct bpf_align_test *test = &tests[i];
696 
697 		if (!test__start_subtest(test->descr))
698 			continue;
699 
700 		ASSERT_OK(do_test_single(test), test->descr);
701 	}
702 }
703