xref: /linux/tools/testing/selftests/bpf/prog_tests/align.c (revision 24bce201d79807b668bf9d9e0aca801c5c0d5f78)
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 
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 
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 
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