xref: /linux/tools/testing/selftests/bpf/progs/verifier_bounds.c (revision 34dc1baba215b826e454b8d19e4f24adbeb7d00d)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/bounds.c */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7 
8 struct {
9 	__uint(type, BPF_MAP_TYPE_HASH);
10 	__uint(max_entries, 1);
11 	__type(key, long long);
12 	__type(value, long long);
13 } map_hash_8b SEC(".maps");
14 
15 SEC("socket")
16 __description("subtraction bounds (map value) variant 1")
17 __failure __msg("R0 max value is outside of the allowed memory range")
18 __failure_unpriv
19 __naked void bounds_map_value_variant_1(void)
20 {
21 	asm volatile ("					\
22 	r1 = 0;						\
23 	*(u64*)(r10 - 8) = r1;				\
24 	r2 = r10;					\
25 	r2 += -8;					\
26 	r1 = %[map_hash_8b] ll;				\
27 	call %[bpf_map_lookup_elem];			\
28 	if r0 == 0 goto l0_%=;				\
29 	r1 = *(u8*)(r0 + 0);				\
30 	if r1 > 0xff goto l0_%=;			\
31 	r3 = *(u8*)(r0 + 1);				\
32 	if r3 > 0xff goto l0_%=;			\
33 	r1 -= r3;					\
34 	r1 >>= 56;					\
35 	r0 += r1;					\
36 	r0 = *(u8*)(r0 + 0);				\
37 	exit;						\
38 l0_%=:	r0 = 0;						\
39 	exit;						\
40 "	:
41 	: __imm(bpf_map_lookup_elem),
42 	  __imm_addr(map_hash_8b)
43 	: __clobber_all);
44 }
45 
46 SEC("socket")
47 __description("subtraction bounds (map value) variant 2")
48 __failure
49 __msg("R0 min value is negative, either use unsigned index or do a if (index >=0) check.")
50 __msg_unpriv("R1 has unknown scalar with mixed signed bounds")
51 __naked void bounds_map_value_variant_2(void)
52 {
53 	asm volatile ("					\
54 	r1 = 0;						\
55 	*(u64*)(r10 - 8) = r1;				\
56 	r2 = r10;					\
57 	r2 += -8;					\
58 	r1 = %[map_hash_8b] ll;				\
59 	call %[bpf_map_lookup_elem];			\
60 	if r0 == 0 goto l0_%=;				\
61 	r1 = *(u8*)(r0 + 0);				\
62 	if r1 > 0xff goto l0_%=;			\
63 	r3 = *(u8*)(r0 + 1);				\
64 	if r3 > 0xff goto l0_%=;			\
65 	r1 -= r3;					\
66 	r0 += r1;					\
67 	r0 = *(u8*)(r0 + 0);				\
68 	exit;						\
69 l0_%=:	r0 = 0;						\
70 	exit;						\
71 "	:
72 	: __imm(bpf_map_lookup_elem),
73 	  __imm_addr(map_hash_8b)
74 	: __clobber_all);
75 }
76 
77 SEC("socket")
78 __description("check subtraction on pointers for unpriv")
79 __success __failure_unpriv __msg_unpriv("R9 pointer -= pointer prohibited")
80 __retval(0)
81 __naked void subtraction_on_pointers_for_unpriv(void)
82 {
83 	asm volatile ("					\
84 	r0 = 0;						\
85 	r1 = %[map_hash_8b] ll;				\
86 	r2 = r10;					\
87 	r2 += -8;					\
88 	r6 = 9;						\
89 	*(u64*)(r2 + 0) = r6;				\
90 	call %[bpf_map_lookup_elem];			\
91 	r9 = r10;					\
92 	r9 -= r0;					\
93 	r1 = %[map_hash_8b] ll;				\
94 	r2 = r10;					\
95 	r2 += -8;					\
96 	r6 = 0;						\
97 	*(u64*)(r2 + 0) = r6;				\
98 	call %[bpf_map_lookup_elem];			\
99 	if r0 != 0 goto l0_%=;				\
100 	exit;						\
101 l0_%=:	*(u64*)(r0 + 0) = r9;				\
102 	r0 = 0;						\
103 	exit;						\
104 "	:
105 	: __imm(bpf_map_lookup_elem),
106 	  __imm_addr(map_hash_8b)
107 	: __clobber_all);
108 }
109 
110 SEC("socket")
111 __description("bounds check based on zero-extended MOV")
112 __success __success_unpriv __retval(0)
113 __naked void based_on_zero_extended_mov(void)
114 {
115 	asm volatile ("					\
116 	r1 = 0;						\
117 	*(u64*)(r10 - 8) = r1;				\
118 	r2 = r10;					\
119 	r2 += -8;					\
120 	r1 = %[map_hash_8b] ll;				\
121 	call %[bpf_map_lookup_elem];			\
122 	if r0 == 0 goto l0_%=;				\
123 	/* r2 = 0x0000'0000'ffff'ffff */		\
124 	w2 = 0xffffffff;				\
125 	/* r2 = 0 */					\
126 	r2 >>= 32;					\
127 	/* no-op */					\
128 	r0 += r2;					\
129 	/* access at offset 0 */			\
130 	r0 = *(u8*)(r0 + 0);				\
131 l0_%=:	/* exit */					\
132 	r0 = 0;						\
133 	exit;						\
134 "	:
135 	: __imm(bpf_map_lookup_elem),
136 	  __imm_addr(map_hash_8b)
137 	: __clobber_all);
138 }
139 
140 SEC("socket")
141 __description("bounds check based on sign-extended MOV. test1")
142 __failure __msg("map_value pointer and 4294967295")
143 __failure_unpriv
144 __naked void on_sign_extended_mov_test1(void)
145 {
146 	asm volatile ("					\
147 	r1 = 0;						\
148 	*(u64*)(r10 - 8) = r1;				\
149 	r2 = r10;					\
150 	r2 += -8;					\
151 	r1 = %[map_hash_8b] ll;				\
152 	call %[bpf_map_lookup_elem];			\
153 	if r0 == 0 goto l0_%=;				\
154 	/* r2 = 0xffff'ffff'ffff'ffff */		\
155 	r2 = 0xffffffff;				\
156 	/* r2 = 0xffff'ffff */				\
157 	r2 >>= 32;					\
158 	/* r0 = <oob pointer> */			\
159 	r0 += r2;					\
160 	/* access to OOB pointer */			\
161 	r0 = *(u8*)(r0 + 0);				\
162 l0_%=:	/* exit */					\
163 	r0 = 0;						\
164 	exit;						\
165 "	:
166 	: __imm(bpf_map_lookup_elem),
167 	  __imm_addr(map_hash_8b)
168 	: __clobber_all);
169 }
170 
171 SEC("socket")
172 __description("bounds check based on sign-extended MOV. test2")
173 __failure __msg("R0 min value is outside of the allowed memory range")
174 __failure_unpriv
175 __naked void on_sign_extended_mov_test2(void)
176 {
177 	asm volatile ("					\
178 	r1 = 0;						\
179 	*(u64*)(r10 - 8) = r1;				\
180 	r2 = r10;					\
181 	r2 += -8;					\
182 	r1 = %[map_hash_8b] ll;				\
183 	call %[bpf_map_lookup_elem];			\
184 	if r0 == 0 goto l0_%=;				\
185 	/* r2 = 0xffff'ffff'ffff'ffff */		\
186 	r2 = 0xffffffff;				\
187 	/* r2 = 0xfff'ffff */				\
188 	r2 >>= 36;					\
189 	/* r0 = <oob pointer> */			\
190 	r0 += r2;					\
191 	/* access to OOB pointer */			\
192 	r0 = *(u8*)(r0 + 0);				\
193 l0_%=:	/* exit */					\
194 	r0 = 0;						\
195 	exit;						\
196 "	:
197 	: __imm(bpf_map_lookup_elem),
198 	  __imm_addr(map_hash_8b)
199 	: __clobber_all);
200 }
201 
202 SEC("tc")
203 __description("bounds check based on reg_off + var_off + insn_off. test1")
204 __failure __msg("value_size=8 off=1073741825")
205 __naked void var_off_insn_off_test1(void)
206 {
207 	asm volatile ("					\
208 	r6 = *(u32*)(r1 + %[__sk_buff_mark]);		\
209 	r1 = 0;						\
210 	*(u64*)(r10 - 8) = r1;				\
211 	r2 = r10;					\
212 	r2 += -8;					\
213 	r1 = %[map_hash_8b] ll;				\
214 	call %[bpf_map_lookup_elem];			\
215 	if r0 == 0 goto l0_%=;				\
216 	r6 &= 1;					\
217 	r6 += %[__imm_0];				\
218 	r0 += r6;					\
219 	r0 += %[__imm_0];				\
220 l0_%=:	r0 = *(u8*)(r0 + 3);				\
221 	r0 = 0;						\
222 	exit;						\
223 "	:
224 	: __imm(bpf_map_lookup_elem),
225 	  __imm_addr(map_hash_8b),
226 	  __imm_const(__imm_0, (1 << 29) - 1),
227 	  __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
228 	: __clobber_all);
229 }
230 
231 SEC("tc")
232 __description("bounds check based on reg_off + var_off + insn_off. test2")
233 __failure __msg("value 1073741823")
234 __naked void var_off_insn_off_test2(void)
235 {
236 	asm volatile ("					\
237 	r6 = *(u32*)(r1 + %[__sk_buff_mark]);		\
238 	r1 = 0;						\
239 	*(u64*)(r10 - 8) = r1;				\
240 	r2 = r10;					\
241 	r2 += -8;					\
242 	r1 = %[map_hash_8b] ll;				\
243 	call %[bpf_map_lookup_elem];			\
244 	if r0 == 0 goto l0_%=;				\
245 	r6 &= 1;					\
246 	r6 += %[__imm_0];				\
247 	r0 += r6;					\
248 	r0 += %[__imm_1];				\
249 l0_%=:	r0 = *(u8*)(r0 + 3);				\
250 	r0 = 0;						\
251 	exit;						\
252 "	:
253 	: __imm(bpf_map_lookup_elem),
254 	  __imm_addr(map_hash_8b),
255 	  __imm_const(__imm_0, (1 << 30) - 1),
256 	  __imm_const(__imm_1, (1 << 29) - 1),
257 	  __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
258 	: __clobber_all);
259 }
260 
261 SEC("socket")
262 __description("bounds check after truncation of non-boundary-crossing range")
263 __success __success_unpriv __retval(0)
264 __naked void of_non_boundary_crossing_range(void)
265 {
266 	asm volatile ("					\
267 	r1 = 0;						\
268 	*(u64*)(r10 - 8) = r1;				\
269 	r2 = r10;					\
270 	r2 += -8;					\
271 	r1 = %[map_hash_8b] ll;				\
272 	call %[bpf_map_lookup_elem];			\
273 	if r0 == 0 goto l0_%=;				\
274 	/* r1 = [0x00, 0xff] */				\
275 	r1 = *(u8*)(r0 + 0);				\
276 	r2 = 1;						\
277 	/* r2 = 0x10'0000'0000 */			\
278 	r2 <<= 36;					\
279 	/* r1 = [0x10'0000'0000, 0x10'0000'00ff] */	\
280 	r1 += r2;					\
281 	/* r1 = [0x10'7fff'ffff, 0x10'8000'00fe] */	\
282 	r1 += 0x7fffffff;				\
283 	/* r1 = [0x00, 0xff] */				\
284 	w1 -= 0x7fffffff;				\
285 	/* r1 = 0 */					\
286 	r1 >>= 8;					\
287 	/* no-op */					\
288 	r0 += r1;					\
289 	/* access at offset 0 */			\
290 	r0 = *(u8*)(r0 + 0);				\
291 l0_%=:	/* exit */					\
292 	r0 = 0;						\
293 	exit;						\
294 "	:
295 	: __imm(bpf_map_lookup_elem),
296 	  __imm_addr(map_hash_8b)
297 	: __clobber_all);
298 }
299 
300 SEC("socket")
301 __description("bounds check after truncation of boundary-crossing range (1)")
302 __failure
303 /* not actually fully unbounded, but the bound is very high */
304 __msg("value -4294967168 makes map_value pointer be out of bounds")
305 __failure_unpriv
306 __naked void of_boundary_crossing_range_1(void)
307 {
308 	asm volatile ("					\
309 	r1 = 0;						\
310 	*(u64*)(r10 - 8) = r1;				\
311 	r2 = r10;					\
312 	r2 += -8;					\
313 	r1 = %[map_hash_8b] ll;				\
314 	call %[bpf_map_lookup_elem];			\
315 	if r0 == 0 goto l0_%=;				\
316 	/* r1 = [0x00, 0xff] */				\
317 	r1 = *(u8*)(r0 + 0);				\
318 	r1 += %[__imm_0];				\
319 	/* r1 = [0xffff'ff80, 0x1'0000'007f] */		\
320 	r1 += %[__imm_0];				\
321 	/* r1 = [0xffff'ff80, 0xffff'ffff] or		\
322 	 *      [0x0000'0000, 0x0000'007f]		\
323 	 */						\
324 	w1 += 0;					\
325 	r1 -= %[__imm_0];				\
326 	/* r1 = [0x00, 0xff] or				\
327 	 *      [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]\
328 	 */						\
329 	r1 -= %[__imm_0];				\
330 	/* error on OOB pointer computation */		\
331 	r0 += r1;					\
332 	/* exit */					\
333 	r0 = 0;						\
334 l0_%=:	exit;						\
335 "	:
336 	: __imm(bpf_map_lookup_elem),
337 	  __imm_addr(map_hash_8b),
338 	  __imm_const(__imm_0, 0xffffff80 >> 1)
339 	: __clobber_all);
340 }
341 
342 SEC("socket")
343 __description("bounds check after truncation of boundary-crossing range (2)")
344 __failure __msg("value -4294967168 makes map_value pointer be out of bounds")
345 __failure_unpriv
346 __naked void of_boundary_crossing_range_2(void)
347 {
348 	asm volatile ("					\
349 	r1 = 0;						\
350 	*(u64*)(r10 - 8) = r1;				\
351 	r2 = r10;					\
352 	r2 += -8;					\
353 	r1 = %[map_hash_8b] ll;				\
354 	call %[bpf_map_lookup_elem];			\
355 	if r0 == 0 goto l0_%=;				\
356 	/* r1 = [0x00, 0xff] */				\
357 	r1 = *(u8*)(r0 + 0);				\
358 	r1 += %[__imm_0];				\
359 	/* r1 = [0xffff'ff80, 0x1'0000'007f] */		\
360 	r1 += %[__imm_0];				\
361 	/* r1 = [0xffff'ff80, 0xffff'ffff] or		\
362 	 *      [0x0000'0000, 0x0000'007f]		\
363 	 * difference to previous test: truncation via MOV32\
364 	 * instead of ALU32.				\
365 	 */						\
366 	w1 = w1;					\
367 	r1 -= %[__imm_0];				\
368 	/* r1 = [0x00, 0xff] or				\
369 	 *      [0xffff'ffff'0000'0080, 0xffff'ffff'ffff'ffff]\
370 	 */						\
371 	r1 -= %[__imm_0];				\
372 	/* error on OOB pointer computation */		\
373 	r0 += r1;					\
374 	/* exit */					\
375 	r0 = 0;						\
376 l0_%=:	exit;						\
377 "	:
378 	: __imm(bpf_map_lookup_elem),
379 	  __imm_addr(map_hash_8b),
380 	  __imm_const(__imm_0, 0xffffff80 >> 1)
381 	: __clobber_all);
382 }
383 
384 SEC("socket")
385 __description("bounds check after wrapping 32-bit addition")
386 __success __success_unpriv __retval(0)
387 __naked void after_wrapping_32_bit_addition(void)
388 {
389 	asm volatile ("					\
390 	r1 = 0;						\
391 	*(u64*)(r10 - 8) = r1;				\
392 	r2 = r10;					\
393 	r2 += -8;					\
394 	r1 = %[map_hash_8b] ll;				\
395 	call %[bpf_map_lookup_elem];			\
396 	if r0 == 0 goto l0_%=;				\
397 	/* r1 = 0x7fff'ffff */				\
398 	r1 = 0x7fffffff;				\
399 	/* r1 = 0xffff'fffe */				\
400 	r1 += 0x7fffffff;				\
401 	/* r1 = 0 */					\
402 	w1 += 2;					\
403 	/* no-op */					\
404 	r0 += r1;					\
405 	/* access at offset 0 */			\
406 	r0 = *(u8*)(r0 + 0);				\
407 l0_%=:	/* exit */					\
408 	r0 = 0;						\
409 	exit;						\
410 "	:
411 	: __imm(bpf_map_lookup_elem),
412 	  __imm_addr(map_hash_8b)
413 	: __clobber_all);
414 }
415 
416 SEC("socket")
417 __description("bounds check after shift with oversized count operand")
418 __failure __msg("R0 max value is outside of the allowed memory range")
419 __failure_unpriv
420 __naked void shift_with_oversized_count_operand(void)
421 {
422 	asm volatile ("					\
423 	r1 = 0;						\
424 	*(u64*)(r10 - 8) = r1;				\
425 	r2 = r10;					\
426 	r2 += -8;					\
427 	r1 = %[map_hash_8b] ll;				\
428 	call %[bpf_map_lookup_elem];			\
429 	if r0 == 0 goto l0_%=;				\
430 	r2 = 32;					\
431 	r1 = 1;						\
432 	/* r1 = (u32)1 << (u32)32 = ? */		\
433 	w1 <<= w2;					\
434 	/* r1 = [0x0000, 0xffff] */			\
435 	r1 &= 0xffff;					\
436 	/* computes unknown pointer, potentially OOB */	\
437 	r0 += r1;					\
438 	/* potentially OOB access */			\
439 	r0 = *(u8*)(r0 + 0);				\
440 l0_%=:	/* exit */					\
441 	r0 = 0;						\
442 	exit;						\
443 "	:
444 	: __imm(bpf_map_lookup_elem),
445 	  __imm_addr(map_hash_8b)
446 	: __clobber_all);
447 }
448 
449 SEC("socket")
450 __description("bounds check after right shift of maybe-negative number")
451 __failure __msg("R0 unbounded memory access")
452 __failure_unpriv
453 __naked void shift_of_maybe_negative_number(void)
454 {
455 	asm volatile ("					\
456 	r1 = 0;						\
457 	*(u64*)(r10 - 8) = r1;				\
458 	r2 = r10;					\
459 	r2 += -8;					\
460 	r1 = %[map_hash_8b] ll;				\
461 	call %[bpf_map_lookup_elem];			\
462 	if r0 == 0 goto l0_%=;				\
463 	/* r1 = [0x00, 0xff] */				\
464 	r1 = *(u8*)(r0 + 0);				\
465 	/* r1 = [-0x01, 0xfe] */			\
466 	r1 -= 1;					\
467 	/* r1 = 0 or 0xff'ffff'ffff'ffff */		\
468 	r1 >>= 8;					\
469 	/* r1 = 0 or 0xffff'ffff'ffff */		\
470 	r1 >>= 8;					\
471 	/* computes unknown pointer, potentially OOB */	\
472 	r0 += r1;					\
473 	/* potentially OOB access */			\
474 	r0 = *(u8*)(r0 + 0);				\
475 l0_%=:	/* exit */					\
476 	r0 = 0;						\
477 	exit;						\
478 "	:
479 	: __imm(bpf_map_lookup_elem),
480 	  __imm_addr(map_hash_8b)
481 	: __clobber_all);
482 }
483 
484 SEC("socket")
485 __description("bounds check after 32-bit right shift with 64-bit input")
486 __failure __msg("math between map_value pointer and 4294967294 is not allowed")
487 __failure_unpriv
488 __naked void shift_with_64_bit_input(void)
489 {
490 	asm volatile ("					\
491 	r1 = 0;						\
492 	*(u64*)(r10 - 8) = r1;				\
493 	r2 = r10;					\
494 	r2 += -8;					\
495 	r1 = %[map_hash_8b] ll;				\
496 	call %[bpf_map_lookup_elem];			\
497 	if r0 == 0 goto l0_%=;				\
498 	r1 = 2;						\
499 	/* r1 = 1<<32 */				\
500 	r1 <<= 31;					\
501 	/* r1 = 0 (NOT 2!) */				\
502 	w1 >>= 31;					\
503 	/* r1 = 0xffff'fffe (NOT 0!) */			\
504 	w1 -= 2;					\
505 	/* error on computing OOB pointer */		\
506 	r0 += r1;					\
507 	/* exit */					\
508 	r0 = 0;						\
509 l0_%=:	exit;						\
510 "	:
511 	: __imm(bpf_map_lookup_elem),
512 	  __imm_addr(map_hash_8b)
513 	: __clobber_all);
514 }
515 
516 SEC("socket")
517 __description("bounds check map access with off+size signed 32bit overflow. test1")
518 __failure __msg("map_value pointer and 2147483646")
519 __failure_unpriv
520 __naked void size_signed_32bit_overflow_test1(void)
521 {
522 	asm volatile ("					\
523 	r1 = 0;						\
524 	*(u64*)(r10 - 8) = r1;				\
525 	r2 = r10;					\
526 	r2 += -8;					\
527 	r1 = %[map_hash_8b] ll;				\
528 	call %[bpf_map_lookup_elem];			\
529 	if r0 != 0 goto l0_%=;				\
530 	exit;						\
531 l0_%=:	r0 += 0x7ffffffe;				\
532 	r0 = *(u64*)(r0 + 0);				\
533 	goto l1_%=;					\
534 l1_%=:	exit;						\
535 "	:
536 	: __imm(bpf_map_lookup_elem),
537 	  __imm_addr(map_hash_8b)
538 	: __clobber_all);
539 }
540 
541 SEC("socket")
542 __description("bounds check map access with off+size signed 32bit overflow. test2")
543 __failure __msg("pointer offset 1073741822")
544 __msg_unpriv("R0 pointer arithmetic of map value goes out of range")
545 __naked void size_signed_32bit_overflow_test2(void)
546 {
547 	asm volatile ("					\
548 	r1 = 0;						\
549 	*(u64*)(r10 - 8) = r1;				\
550 	r2 = r10;					\
551 	r2 += -8;					\
552 	r1 = %[map_hash_8b] ll;				\
553 	call %[bpf_map_lookup_elem];			\
554 	if r0 != 0 goto l0_%=;				\
555 	exit;						\
556 l0_%=:	r0 += 0x1fffffff;				\
557 	r0 += 0x1fffffff;				\
558 	r0 += 0x1fffffff;				\
559 	r0 = *(u64*)(r0 + 0);				\
560 	goto l1_%=;					\
561 l1_%=:	exit;						\
562 "	:
563 	: __imm(bpf_map_lookup_elem),
564 	  __imm_addr(map_hash_8b)
565 	: __clobber_all);
566 }
567 
568 SEC("socket")
569 __description("bounds check map access with off+size signed 32bit overflow. test3")
570 __failure __msg("pointer offset -1073741822")
571 __msg_unpriv("R0 pointer arithmetic of map value goes out of range")
572 __naked void size_signed_32bit_overflow_test3(void)
573 {
574 	asm volatile ("					\
575 	r1 = 0;						\
576 	*(u64*)(r10 - 8) = r1;				\
577 	r2 = r10;					\
578 	r2 += -8;					\
579 	r1 = %[map_hash_8b] ll;				\
580 	call %[bpf_map_lookup_elem];			\
581 	if r0 != 0 goto l0_%=;				\
582 	exit;						\
583 l0_%=:	r0 -= 0x1fffffff;				\
584 	r0 -= 0x1fffffff;				\
585 	r0 = *(u64*)(r0 + 2);				\
586 	goto l1_%=;					\
587 l1_%=:	exit;						\
588 "	:
589 	: __imm(bpf_map_lookup_elem),
590 	  __imm_addr(map_hash_8b)
591 	: __clobber_all);
592 }
593 
594 SEC("socket")
595 __description("bounds check map access with off+size signed 32bit overflow. test4")
596 __failure __msg("map_value pointer and 1000000000000")
597 __failure_unpriv
598 __naked void size_signed_32bit_overflow_test4(void)
599 {
600 	asm volatile ("					\
601 	r1 = 0;						\
602 	*(u64*)(r10 - 8) = r1;				\
603 	r2 = r10;					\
604 	r2 += -8;					\
605 	r1 = %[map_hash_8b] ll;				\
606 	call %[bpf_map_lookup_elem];			\
607 	if r0 != 0 goto l0_%=;				\
608 	exit;						\
609 l0_%=:	r1 = 1000000;					\
610 	r1 *= 1000000;					\
611 	r0 += r1;					\
612 	r0 = *(u64*)(r0 + 2);				\
613 	goto l1_%=;					\
614 l1_%=:	exit;						\
615 "	:
616 	: __imm(bpf_map_lookup_elem),
617 	  __imm_addr(map_hash_8b)
618 	: __clobber_all);
619 }
620 
621 SEC("socket")
622 __description("bounds check mixed 32bit and 64bit arithmetic. test1")
623 __success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'")
624 __retval(0)
625 __naked void _32bit_and_64bit_arithmetic_test1(void)
626 {
627 	asm volatile ("					\
628 	r0 = 0;						\
629 	r1 = -1;					\
630 	r1 <<= 32;					\
631 	r1 += 1;					\
632 	/* r1 = 0xffffFFFF00000001 */			\
633 	if w1 > 1 goto l0_%=;				\
634 	/* check ALU64 op keeps 32bit bounds */		\
635 	r1 += 1;					\
636 	if w1 > 2 goto l0_%=;				\
637 	goto l1_%=;					\
638 l0_%=:	/* invalid ldx if bounds are lost above */	\
639 	r0 = *(u64*)(r0 - 1);				\
640 l1_%=:	exit;						\
641 "	::: __clobber_all);
642 }
643 
644 SEC("socket")
645 __description("bounds check mixed 32bit and 64bit arithmetic. test2")
646 __success __failure_unpriv __msg_unpriv("R0 invalid mem access 'scalar'")
647 __retval(0)
648 __naked void _32bit_and_64bit_arithmetic_test2(void)
649 {
650 	asm volatile ("					\
651 	r0 = 0;						\
652 	r1 = -1;					\
653 	r1 <<= 32;					\
654 	r1 += 1;					\
655 	/* r1 = 0xffffFFFF00000001 */			\
656 	r2 = 3;						\
657 	/* r1 = 0x2 */					\
658 	w1 += 1;					\
659 	/* check ALU32 op zero extends 64bit bounds */	\
660 	if r1 > r2 goto l0_%=;				\
661 	goto l1_%=;					\
662 l0_%=:	/* invalid ldx if bounds are lost above */	\
663 	r0 = *(u64*)(r0 - 1);				\
664 l1_%=:	exit;						\
665 "	::: __clobber_all);
666 }
667 
668 SEC("tc")
669 __description("assigning 32bit bounds to 64bit for wA = 0, wB = wA")
670 __success __retval(0) __flag(BPF_F_ANY_ALIGNMENT)
671 __naked void for_wa_0_wb_wa(void)
672 {
673 	asm volatile ("					\
674 	r8 = *(u32*)(r1 + %[__sk_buff_data_end]);	\
675 	r7 = *(u32*)(r1 + %[__sk_buff_data]);		\
676 	w9 = 0;						\
677 	w2 = w9;					\
678 	r6 = r7;					\
679 	r6 += r2;					\
680 	r3 = r6;					\
681 	r3 += 8;					\
682 	if r3 > r8 goto l0_%=;				\
683 	r5 = *(u32*)(r6 + 0);				\
684 l0_%=:	r0 = 0;						\
685 	exit;						\
686 "	:
687 	: __imm_const(__sk_buff_data, offsetof(struct __sk_buff, data)),
688 	  __imm_const(__sk_buff_data_end, offsetof(struct __sk_buff, data_end))
689 	: __clobber_all);
690 }
691 
692 SEC("socket")
693 __description("bounds check for reg = 0, reg xor 1")
694 __success __failure_unpriv
695 __msg_unpriv("R0 min value is outside of the allowed memory range")
696 __retval(0)
697 __naked void reg_0_reg_xor_1(void)
698 {
699 	asm volatile ("					\
700 	r1 = 0;						\
701 	*(u64*)(r10 - 8) = r1;				\
702 	r2 = r10;					\
703 	r2 += -8;					\
704 	r1 = %[map_hash_8b] ll;				\
705 	call %[bpf_map_lookup_elem];			\
706 	if r0 != 0 goto l0_%=;				\
707 	exit;						\
708 l0_%=:	r1 = 0;						\
709 	r1 ^= 1;					\
710 	if r1 != 0 goto l1_%=;				\
711 	r0 = *(u64*)(r0 + 8);				\
712 l1_%=:	r0 = 0;						\
713 	exit;						\
714 "	:
715 	: __imm(bpf_map_lookup_elem),
716 	  __imm_addr(map_hash_8b)
717 	: __clobber_all);
718 }
719 
720 SEC("socket")
721 __description("bounds check for reg32 = 0, reg32 xor 1")
722 __success __failure_unpriv
723 __msg_unpriv("R0 min value is outside of the allowed memory range")
724 __retval(0)
725 __naked void reg32_0_reg32_xor_1(void)
726 {
727 	asm volatile ("					\
728 	r1 = 0;						\
729 	*(u64*)(r10 - 8) = r1;				\
730 	r2 = r10;					\
731 	r2 += -8;					\
732 	r1 = %[map_hash_8b] ll;				\
733 	call %[bpf_map_lookup_elem];			\
734 	if r0 != 0 goto l0_%=;				\
735 	exit;						\
736 l0_%=:	w1 = 0;						\
737 	w1 ^= 1;					\
738 	if w1 != 0 goto l1_%=;				\
739 	r0 = *(u64*)(r0 + 8);				\
740 l1_%=:	r0 = 0;						\
741 	exit;						\
742 "	:
743 	: __imm(bpf_map_lookup_elem),
744 	  __imm_addr(map_hash_8b)
745 	: __clobber_all);
746 }
747 
748 SEC("socket")
749 __description("bounds check for reg = 2, reg xor 3")
750 __success __failure_unpriv
751 __msg_unpriv("R0 min value is outside of the allowed memory range")
752 __retval(0)
753 __naked void reg_2_reg_xor_3(void)
754 {
755 	asm volatile ("					\
756 	r1 = 0;						\
757 	*(u64*)(r10 - 8) = r1;				\
758 	r2 = r10;					\
759 	r2 += -8;					\
760 	r1 = %[map_hash_8b] ll;				\
761 	call %[bpf_map_lookup_elem];			\
762 	if r0 != 0 goto l0_%=;				\
763 	exit;						\
764 l0_%=:	r1 = 2;						\
765 	r1 ^= 3;					\
766 	if r1 > 0 goto l1_%=;				\
767 	r0 = *(u64*)(r0 + 8);				\
768 l1_%=:	r0 = 0;						\
769 	exit;						\
770 "	:
771 	: __imm(bpf_map_lookup_elem),
772 	  __imm_addr(map_hash_8b)
773 	: __clobber_all);
774 }
775 
776 SEC("socket")
777 __description("bounds check for reg = any, reg xor 3")
778 __failure __msg("invalid access to map value")
779 __msg_unpriv("invalid access to map value")
780 __naked void reg_any_reg_xor_3(void)
781 {
782 	asm volatile ("					\
783 	r1 = 0;						\
784 	*(u64*)(r10 - 8) = r1;				\
785 	r2 = r10;					\
786 	r2 += -8;					\
787 	r1 = %[map_hash_8b] ll;				\
788 	call %[bpf_map_lookup_elem];			\
789 	if r0 != 0 goto l0_%=;				\
790 	exit;						\
791 l0_%=:	r1 = *(u64*)(r0 + 0);				\
792 	r1 ^= 3;					\
793 	if r1 != 0 goto l1_%=;				\
794 	r0 = *(u64*)(r0 + 8);				\
795 l1_%=:	r0 = 0;						\
796 	exit;						\
797 "	:
798 	: __imm(bpf_map_lookup_elem),
799 	  __imm_addr(map_hash_8b)
800 	: __clobber_all);
801 }
802 
803 SEC("socket")
804 __description("bounds check for reg32 = any, reg32 xor 3")
805 __failure __msg("invalid access to map value")
806 __msg_unpriv("invalid access to map value")
807 __naked void reg32_any_reg32_xor_3(void)
808 {
809 	asm volatile ("					\
810 	r1 = 0;						\
811 	*(u64*)(r10 - 8) = r1;				\
812 	r2 = r10;					\
813 	r2 += -8;					\
814 	r1 = %[map_hash_8b] ll;				\
815 	call %[bpf_map_lookup_elem];			\
816 	if r0 != 0 goto l0_%=;				\
817 	exit;						\
818 l0_%=:	r1 = *(u64*)(r0 + 0);				\
819 	w1 ^= 3;					\
820 	if w1 != 0 goto l1_%=;				\
821 	r0 = *(u64*)(r0 + 8);				\
822 l1_%=:	r0 = 0;						\
823 	exit;						\
824 "	:
825 	: __imm(bpf_map_lookup_elem),
826 	  __imm_addr(map_hash_8b)
827 	: __clobber_all);
828 }
829 
830 SEC("socket")
831 __description("bounds check for reg > 0, reg xor 3")
832 __success __failure_unpriv
833 __msg_unpriv("R0 min value is outside of the allowed memory range")
834 __retval(0)
835 __naked void reg_0_reg_xor_3(void)
836 {
837 	asm volatile ("					\
838 	r1 = 0;						\
839 	*(u64*)(r10 - 8) = r1;				\
840 	r2 = r10;					\
841 	r2 += -8;					\
842 	r1 = %[map_hash_8b] ll;				\
843 	call %[bpf_map_lookup_elem];			\
844 	if r0 != 0 goto l0_%=;				\
845 	exit;						\
846 l0_%=:	r1 = *(u64*)(r0 + 0);				\
847 	if r1 <= 0 goto l1_%=;				\
848 	r1 ^= 3;					\
849 	if r1 >= 0 goto l1_%=;				\
850 	r0 = *(u64*)(r0 + 8);				\
851 l1_%=:	r0 = 0;						\
852 	exit;						\
853 "	:
854 	: __imm(bpf_map_lookup_elem),
855 	  __imm_addr(map_hash_8b)
856 	: __clobber_all);
857 }
858 
859 SEC("socket")
860 __description("bounds check for reg32 > 0, reg32 xor 3")
861 __success __failure_unpriv
862 __msg_unpriv("R0 min value is outside of the allowed memory range")
863 __retval(0)
864 __naked void reg32_0_reg32_xor_3(void)
865 {
866 	asm volatile ("					\
867 	r1 = 0;						\
868 	*(u64*)(r10 - 8) = r1;				\
869 	r2 = r10;					\
870 	r2 += -8;					\
871 	r1 = %[map_hash_8b] ll;				\
872 	call %[bpf_map_lookup_elem];			\
873 	if r0 != 0 goto l0_%=;				\
874 	exit;						\
875 l0_%=:	r1 = *(u64*)(r0 + 0);				\
876 	if w1 <= 0 goto l1_%=;				\
877 	w1 ^= 3;					\
878 	if w1 >= 0 goto l1_%=;				\
879 	r0 = *(u64*)(r0 + 8);				\
880 l1_%=:	r0 = 0;						\
881 	exit;						\
882 "	:
883 	: __imm(bpf_map_lookup_elem),
884 	  __imm_addr(map_hash_8b)
885 	: __clobber_all);
886 }
887 
888 SEC("socket")
889 __description("bounds checks after 32-bit truncation. test 1")
890 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
891 __retval(0)
892 __naked void _32_bit_truncation_test_1(void)
893 {
894 	asm volatile ("					\
895 	r1 = 0;						\
896 	*(u64*)(r10 - 8) = r1;				\
897 	r2 = r10;					\
898 	r2 += -8;					\
899 	r1 = %[map_hash_8b] ll;				\
900 	call %[bpf_map_lookup_elem];			\
901 	if r0 == 0 goto l0_%=;				\
902 	r1 = *(u32*)(r0 + 0);				\
903 	/* This used to reduce the max bound to 0x7fffffff */\
904 	if r1 == 0 goto l1_%=;				\
905 	if r1 > 0x7fffffff goto l0_%=;			\
906 l1_%=:	r0 = 0;						\
907 l0_%=:	exit;						\
908 "	:
909 	: __imm(bpf_map_lookup_elem),
910 	  __imm_addr(map_hash_8b)
911 	: __clobber_all);
912 }
913 
914 SEC("socket")
915 __description("bounds checks after 32-bit truncation. test 2")
916 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
917 __retval(0)
918 __naked void _32_bit_truncation_test_2(void)
919 {
920 	asm volatile ("					\
921 	r1 = 0;						\
922 	*(u64*)(r10 - 8) = r1;				\
923 	r2 = r10;					\
924 	r2 += -8;					\
925 	r1 = %[map_hash_8b] ll;				\
926 	call %[bpf_map_lookup_elem];			\
927 	if r0 == 0 goto l0_%=;				\
928 	r1 = *(u32*)(r0 + 0);				\
929 	if r1 s< 1 goto l1_%=;				\
930 	if w1 s< 0 goto l0_%=;				\
931 l1_%=:	r0 = 0;						\
932 l0_%=:	exit;						\
933 "	:
934 	: __imm(bpf_map_lookup_elem),
935 	  __imm_addr(map_hash_8b)
936 	: __clobber_all);
937 }
938 
939 SEC("xdp")
940 __description("bound check with JMP_JLT for crossing 64-bit signed boundary")
941 __success __retval(0)
942 __naked void crossing_64_bit_signed_boundary_1(void)
943 {
944 	asm volatile ("					\
945 	r2 = *(u32*)(r1 + %[xdp_md_data]);		\
946 	r3 = *(u32*)(r1 + %[xdp_md_data_end]);		\
947 	r1 = r2;					\
948 	r1 += 1;					\
949 	if r1 > r3 goto l0_%=;				\
950 	r1 = *(u8*)(r2 + 0);				\
951 	r0 = 0x7fffffffffffff10 ll;			\
952 	r1 += r0;					\
953 	r0 = 0x8000000000000000 ll;			\
954 l1_%=:	r0 += 1;					\
955 	/* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\
956 	if r0 < r1 goto l1_%=;				\
957 l0_%=:	r0 = 0;						\
958 	exit;						\
959 "	:
960 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
961 	  __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
962 	: __clobber_all);
963 }
964 
965 SEC("xdp")
966 __description("bound check with JMP_JSLT for crossing 64-bit signed boundary")
967 __success __retval(0)
968 __naked void crossing_64_bit_signed_boundary_2(void)
969 {
970 	asm volatile ("					\
971 	r2 = *(u32*)(r1 + %[xdp_md_data]);		\
972 	r3 = *(u32*)(r1 + %[xdp_md_data_end]);		\
973 	r1 = r2;					\
974 	r1 += 1;					\
975 	if r1 > r3 goto l0_%=;				\
976 	r1 = *(u8*)(r2 + 0);				\
977 	r0 = 0x7fffffffffffff10 ll;			\
978 	r1 += r0;					\
979 	r2 = 0x8000000000000fff ll;			\
980 	r0 = 0x8000000000000000 ll;			\
981 l1_%=:	r0 += 1;					\
982 	if r0 s> r2 goto l0_%=;				\
983 	/* r1 signed range is [S64_MIN, S64_MAX] */	\
984 	if r0 s< r1 goto l1_%=;				\
985 	r0 = 1;						\
986 	exit;						\
987 l0_%=:	r0 = 0;						\
988 	exit;						\
989 "	:
990 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
991 	  __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
992 	: __clobber_all);
993 }
994 
995 SEC("xdp")
996 __description("bound check for loop upper bound greater than U32_MAX")
997 __success __retval(0)
998 __naked void bound_greater_than_u32_max(void)
999 {
1000 	asm volatile ("					\
1001 	r2 = *(u32*)(r1 + %[xdp_md_data]);		\
1002 	r3 = *(u32*)(r1 + %[xdp_md_data_end]);		\
1003 	r1 = r2;					\
1004 	r1 += 1;					\
1005 	if r1 > r3 goto l0_%=;				\
1006 	r1 = *(u8*)(r2 + 0);				\
1007 	r0 = 0x100000000 ll;				\
1008 	r1 += r0;					\
1009 	r0 = 0x100000000 ll;				\
1010 l1_%=:	r0 += 1;					\
1011 	if r0 < r1 goto l1_%=;				\
1012 l0_%=:	r0 = 0;						\
1013 	exit;						\
1014 "	:
1015 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1016 	  __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1017 	: __clobber_all);
1018 }
1019 
1020 SEC("xdp")
1021 __description("bound check with JMP32_JLT for crossing 32-bit signed boundary")
1022 __success __retval(0)
1023 __naked void crossing_32_bit_signed_boundary_1(void)
1024 {
1025 	asm volatile ("					\
1026 	r2 = *(u32*)(r1 + %[xdp_md_data]);		\
1027 	r3 = *(u32*)(r1 + %[xdp_md_data_end]);		\
1028 	r1 = r2;					\
1029 	r1 += 1;					\
1030 	if r1 > r3 goto l0_%=;				\
1031 	r1 = *(u8*)(r2 + 0);				\
1032 	w0 = 0x7fffff10;				\
1033 	w1 += w0;					\
1034 	w0 = 0x80000000;				\
1035 l1_%=:	w0 += 1;					\
1036 	/* r1 unsigned range is [0, 0x8000000f] */	\
1037 	if w0 < w1 goto l1_%=;				\
1038 l0_%=:	r0 = 0;						\
1039 	exit;						\
1040 "	:
1041 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1042 	  __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1043 	: __clobber_all);
1044 }
1045 
1046 SEC("xdp")
1047 __description("bound check with JMP32_JSLT for crossing 32-bit signed boundary")
1048 __success __retval(0)
1049 __naked void crossing_32_bit_signed_boundary_2(void)
1050 {
1051 	asm volatile ("					\
1052 	r2 = *(u32*)(r1 + %[xdp_md_data]);		\
1053 	r3 = *(u32*)(r1 + %[xdp_md_data_end]);		\
1054 	r1 = r2;					\
1055 	r1 += 1;					\
1056 	if r1 > r3 goto l0_%=;				\
1057 	r1 = *(u8*)(r2 + 0);				\
1058 	w0 = 0x7fffff10;				\
1059 	w1 += w0;					\
1060 	w2 = 0x80000fff;				\
1061 	w0 = 0x80000000;				\
1062 l1_%=:	w0 += 1;					\
1063 	if w0 s> w2 goto l0_%=;				\
1064 	/* r1 signed range is [S32_MIN, S32_MAX] */	\
1065 	if w0 s< w1 goto l1_%=;				\
1066 	r0 = 1;						\
1067 	exit;						\
1068 l0_%=:	r0 = 0;						\
1069 	exit;						\
1070 "	:
1071 	: __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1072 	  __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1073 	: __clobber_all);
1074 }
1075 
1076 char _license[] SEC("license") = "GPL";
1077