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