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
8struct {
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
15SEC("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; \
38l0_%=: r0 = 0; \
39 exit; \
40" :
41 : __imm(bpf_map_lookup_elem),
42 __imm_addr(map_hash_8b)
43 : __clobber_all);
44}
45
46SEC("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; \
69l0_%=: r0 = 0; \
70 exit; \
71" :
72 : __imm(bpf_map_lookup_elem),
73 __imm_addr(map_hash_8b)
74 : __clobber_all);
75}
76
77SEC("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; \
101l0_%=: *(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
110SEC("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); \
131l0_%=: /* exit */ \
132 r0 = 0; \
133 exit; \
134" :
135 : __imm(bpf_map_lookup_elem),
136 __imm_addr(map_hash_8b)
137 : __clobber_all);
138}
139
140SEC("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); \
162l0_%=: /* exit */ \
163 r0 = 0; \
164 exit; \
165" :
166 : __imm(bpf_map_lookup_elem),
167 __imm_addr(map_hash_8b)
168 : __clobber_all);
169}
170
171SEC("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); \
193l0_%=: /* exit */ \
194 r0 = 0; \
195 exit; \
196" :
197 : __imm(bpf_map_lookup_elem),
198 __imm_addr(map_hash_8b)
199 : __clobber_all);
200}
201
202SEC("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]; \
220l0_%=: 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
231SEC("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]; \
249l0_%=: 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
261SEC("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); \
291l0_%=: /* exit */ \
292 r0 = 0; \
293 exit; \
294" :
295 : __imm(bpf_map_lookup_elem),
296 __imm_addr(map_hash_8b)
297 : __clobber_all);
298}
299
300SEC("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; \
334l0_%=: 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
342SEC("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; \
376l0_%=: 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
384SEC("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); \
407l0_%=: /* exit */ \
408 r0 = 0; \
409 exit; \
410" :
411 : __imm(bpf_map_lookup_elem),
412 __imm_addr(map_hash_8b)
413 : __clobber_all);
414}
415
416SEC("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); \
440l0_%=: /* exit */ \
441 r0 = 0; \
442 exit; \
443" :
444 : __imm(bpf_map_lookup_elem),
445 __imm_addr(map_hash_8b)
446 : __clobber_all);
447}
448
449SEC("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); \
475l0_%=: /* exit */ \
476 r0 = 0; \
477 exit; \
478" :
479 : __imm(bpf_map_lookup_elem),
480 __imm_addr(map_hash_8b)
481 : __clobber_all);
482}
483
484SEC("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; \
509l0_%=: exit; \
510" :
511 : __imm(bpf_map_lookup_elem),
512 __imm_addr(map_hash_8b)
513 : __clobber_all);
514}
515
516SEC("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; \
531l0_%=: r0 += 0x7ffffffe; \
532 r0 = *(u64*)(r0 + 0); \
533 goto l1_%=; \
534l1_%=: exit; \
535" :
536 : __imm(bpf_map_lookup_elem),
537 __imm_addr(map_hash_8b)
538 : __clobber_all);
539}
540
541SEC("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; \
556l0_%=: r0 += 0x1fffffff; \
557 r0 += 0x1fffffff; \
558 r0 += 0x1fffffff; \
559 r0 = *(u64*)(r0 + 0); \
560 goto l1_%=; \
561l1_%=: exit; \
562" :
563 : __imm(bpf_map_lookup_elem),
564 __imm_addr(map_hash_8b)
565 : __clobber_all);
566}
567
568SEC("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; \
583l0_%=: r0 -= 0x1fffffff; \
584 r0 -= 0x1fffffff; \
585 r0 = *(u64*)(r0 + 2); \
586 goto l1_%=; \
587l1_%=: exit; \
588" :
589 : __imm(bpf_map_lookup_elem),
590 __imm_addr(map_hash_8b)
591 : __clobber_all);
592}
593
594SEC("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; \
609l0_%=: r1 = 1000000; \
610 r1 *= 1000000; \
611 r0 += r1; \
612 r0 = *(u64*)(r0 + 2); \
613 goto l1_%=; \
614l1_%=: exit; \
615" :
616 : __imm(bpf_map_lookup_elem),
617 __imm_addr(map_hash_8b)
618 : __clobber_all);
619}
620
621SEC("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_%=; \
638l0_%=: /* invalid ldx if bounds are lost above */ \
639 r0 = *(u64*)(r0 - 1); \
640l1_%=: exit; \
641" ::: __clobber_all);
642}
643
644SEC("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_%=; \
662l0_%=: /* invalid ldx if bounds are lost above */ \
663 r0 = *(u64*)(r0 - 1); \
664l1_%=: exit; \
665" ::: __clobber_all);
666}
667
668SEC("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); \
684l0_%=: 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
692SEC("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; \
708l0_%=: r1 = 0; \
709 r1 ^= 1; \
710 if r1 != 0 goto l1_%=; \
711 r0 = *(u64*)(r0 + 8); \
712l1_%=: r0 = 0; \
713 exit; \
714" :
715 : __imm(bpf_map_lookup_elem),
716 __imm_addr(map_hash_8b)
717 : __clobber_all);
718}
719
720SEC("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; \
736l0_%=: w1 = 0; \
737 w1 ^= 1; \
738 if w1 != 0 goto l1_%=; \
739 r0 = *(u64*)(r0 + 8); \
740l1_%=: r0 = 0; \
741 exit; \
742" :
743 : __imm(bpf_map_lookup_elem),
744 __imm_addr(map_hash_8b)
745 : __clobber_all);
746}
747
748SEC("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; \
764l0_%=: r1 = 2; \
765 r1 ^= 3; \
766 if r1 > 0 goto l1_%=; \
767 r0 = *(u64*)(r0 + 8); \
768l1_%=: r0 = 0; \
769 exit; \
770" :
771 : __imm(bpf_map_lookup_elem),
772 __imm_addr(map_hash_8b)
773 : __clobber_all);
774}
775
776SEC("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; \
791l0_%=: r1 = *(u64*)(r0 + 0); \
792 r1 ^= 3; \
793 if r1 != 0 goto l1_%=; \
794 r0 = *(u64*)(r0 + 8); \
795l1_%=: r0 = 0; \
796 exit; \
797" :
798 : __imm(bpf_map_lookup_elem),
799 __imm_addr(map_hash_8b)
800 : __clobber_all);
801}
802
803SEC("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; \
818l0_%=: r1 = *(u64*)(r0 + 0); \
819 w1 ^= 3; \
820 if w1 != 0 goto l1_%=; \
821 r0 = *(u64*)(r0 + 8); \
822l1_%=: r0 = 0; \
823 exit; \
824" :
825 : __imm(bpf_map_lookup_elem),
826 __imm_addr(map_hash_8b)
827 : __clobber_all);
828}
829
830SEC("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; \
846l0_%=: r1 = *(u64*)(r0 + 0); \
847 if r1 <= 0 goto l1_%=; \
848 r1 ^= 3; \
849 if r1 >= 0 goto l1_%=; \
850 r0 = *(u64*)(r0 + 8); \
851l1_%=: r0 = 0; \
852 exit; \
853" :
854 : __imm(bpf_map_lookup_elem),
855 __imm_addr(map_hash_8b)
856 : __clobber_all);
857}
858
859SEC("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; \
875l0_%=: r1 = *(u64*)(r0 + 0); \
876 if w1 <= 0 goto l1_%=; \
877 w1 ^= 3; \
878 if w1 >= 0 goto l1_%=; \
879 r0 = *(u64*)(r0 + 8); \
880l1_%=: r0 = 0; \
881 exit; \
882" :
883 : __imm(bpf_map_lookup_elem),
884 __imm_addr(map_hash_8b)
885 : __clobber_all);
886}
887
888SEC("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_%=; \
906l1_%=: r0 = 0; \
907l0_%=: exit; \
908" :
909 : __imm(bpf_map_lookup_elem),
910 __imm_addr(map_hash_8b)
911 : __clobber_all);
912}
913
914SEC("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_%=; \
931l1_%=: r0 = 0; \
932l0_%=: exit; \
933" :
934 : __imm(bpf_map_lookup_elem),
935 __imm_addr(map_hash_8b)
936 : __clobber_all);
937}
938
939SEC("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; \
954l1_%=: r0 += 1; \
955 /* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\
956 if r0 < r1 goto l1_%=; \
957l0_%=: 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
965SEC("xdp")
966__description("bound check with JMP_JSLT for crossing 64-bit signed boundary")
967__success __retval(0)
968__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */
969__naked void crossing_64_bit_signed_boundary_2(void)
970{
971 asm volatile (" \
972 r2 = *(u32*)(r1 + %[xdp_md_data]); \
973 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
974 r1 = r2; \
975 r1 += 1; \
976 if r1 > r3 goto l0_%=; \
977 r1 = *(u8*)(r2 + 0); \
978 r0 = 0x7fffffffffffff10 ll; \
979 r1 += r0; \
980 r2 = 0x8000000000000fff ll; \
981 r0 = 0x8000000000000000 ll; \
982l1_%=: r0 += 1; \
983 if r0 s> r2 goto l0_%=; \
984 /* r1 signed range is [S64_MIN, S64_MAX] */ \
985 if r0 s< r1 goto l1_%=; \
986 r0 = 1; \
987 exit; \
988l0_%=: r0 = 0; \
989 exit; \
990" :
991 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
992 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
993 : __clobber_all);
994}
995
996SEC("xdp")
997__description("bound check for loop upper bound greater than U32_MAX")
998__success __retval(0)
999__naked void bound_greater_than_u32_max(void)
1000{
1001 asm volatile (" \
1002 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1003 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1004 r1 = r2; \
1005 r1 += 1; \
1006 if r1 > r3 goto l0_%=; \
1007 r1 = *(u8*)(r2 + 0); \
1008 r0 = 0x100000000 ll; \
1009 r1 += r0; \
1010 r0 = 0x100000000 ll; \
1011l1_%=: r0 += 1; \
1012 if r0 < r1 goto l1_%=; \
1013l0_%=: r0 = 0; \
1014 exit; \
1015" :
1016 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1017 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1018 : __clobber_all);
1019}
1020
1021SEC("xdp")
1022__description("bound check with JMP32_JLT for crossing 32-bit signed boundary")
1023__success __retval(0)
1024__naked void crossing_32_bit_signed_boundary_1(void)
1025{
1026 asm volatile (" \
1027 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1028 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1029 r1 = r2; \
1030 r1 += 1; \
1031 if r1 > r3 goto l0_%=; \
1032 r1 = *(u8*)(r2 + 0); \
1033 w0 = 0x7fffff10; \
1034 w1 += w0; \
1035 w0 = 0x80000000; \
1036l1_%=: w0 += 1; \
1037 /* r1 unsigned range is [0, 0x8000000f] */ \
1038 if w0 < w1 goto l1_%=; \
1039l0_%=: r0 = 0; \
1040 exit; \
1041" :
1042 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1043 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1044 : __clobber_all);
1045}
1046
1047SEC("xdp")
1048__description("bound check with JMP32_JSLT for crossing 32-bit signed boundary")
1049__success __retval(0)
1050__flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */
1051__naked void crossing_32_bit_signed_boundary_2(void)
1052{
1053 asm volatile (" \
1054 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1055 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1056 r1 = r2; \
1057 r1 += 1; \
1058 if r1 > r3 goto l0_%=; \
1059 r1 = *(u8*)(r2 + 0); \
1060 w0 = 0x7fffff10; \
1061 w1 += w0; \
1062 w2 = 0x80000fff; \
1063 w0 = 0x80000000; \
1064l1_%=: w0 += 1; \
1065 if w0 s> w2 goto l0_%=; \
1066 /* r1 signed range is [S32_MIN, S32_MAX] */ \
1067 if w0 s< w1 goto l1_%=; \
1068 r0 = 1; \
1069 exit; \
1070l0_%=: r0 = 0; \
1071 exit; \
1072" :
1073 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1074 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1075 : __clobber_all);
1076}
1077
1078SEC("tc")
1079__description("bounds check with JMP_NE for reg edge")
1080__success __retval(0)
1081__naked void reg_not_equal_const(void)
1082{
1083 asm volatile (" \
1084 r6 = r1; \
1085 r1 = 0; \
1086 *(u64*)(r10 - 8) = r1; \
1087 call %[bpf_get_prandom_u32]; \
1088 r4 = r0; \
1089 r4 &= 7; \
1090 if r4 != 0 goto l0_%=; \
1091 r0 = 0; \
1092 exit; \
1093l0_%=: r1 = r6; \
1094 r2 = 0; \
1095 r3 = r10; \
1096 r3 += -8; \
1097 r5 = 0; \
1098 /* The 4th argument of bpf_skb_store_bytes is defined as \
1099 * ARG_CONST_SIZE, so 0 is not allowed. The 'r4 != 0' \
1100 * is providing us this exclusion of zero from initial \
1101 * [0, 7] range. \
1102 */ \
1103 call %[bpf_skb_store_bytes]; \
1104 r0 = 0; \
1105 exit; \
1106" :
1107 : __imm(bpf_get_prandom_u32),
1108 __imm(bpf_skb_store_bytes)
1109 : __clobber_all);
1110}
1111
1112SEC("tc")
1113__description("bounds check with JMP_EQ for reg edge")
1114__success __retval(0)
1115__naked void reg_equal_const(void)
1116{
1117 asm volatile (" \
1118 r6 = r1; \
1119 r1 = 0; \
1120 *(u64*)(r10 - 8) = r1; \
1121 call %[bpf_get_prandom_u32]; \
1122 r4 = r0; \
1123 r4 &= 7; \
1124 if r4 == 0 goto l0_%=; \
1125 r1 = r6; \
1126 r2 = 0; \
1127 r3 = r10; \
1128 r3 += -8; \
1129 r5 = 0; \
1130 /* Just the same as what we do in reg_not_equal_const() */ \
1131 call %[bpf_skb_store_bytes]; \
1132l0_%=: r0 = 0; \
1133 exit; \
1134" :
1135 : __imm(bpf_get_prandom_u32),
1136 __imm(bpf_skb_store_bytes)
1137 : __clobber_all);
1138}
1139
1140char _license[] SEC("license") = "GPL";
1141

source code of linux/tools/testing/selftests/bpf/progs/verifier_bounds.c