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
bounds_map_value_variant_1(void)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")
bounds_map_value_variant_2(void)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)
subtraction_on_pointers_for_unpriv(void)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)
based_on_zero_extended_mov(void)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
on_sign_extended_mov_test1(void)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
on_sign_extended_mov_test2(void)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")
var_off_insn_off_test1(void)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")
var_off_insn_off_test2(void)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)
of_non_boundary_crossing_range(void)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
of_boundary_crossing_range_1(void)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
of_boundary_crossing_range_2(void)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)
after_wrapping_32_bit_addition(void)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
shift_with_oversized_count_operand(void)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
shift_of_maybe_negative_number(void)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
shift_with_64_bit_input(void)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
size_signed_32bit_overflow_test1(void)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")
size_signed_32bit_overflow_test2(void)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")
size_signed_32bit_overflow_test3(void)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
size_signed_32bit_overflow_test4(void)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)
_32bit_and_64bit_arithmetic_test1(void)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)
_32bit_and_64bit_arithmetic_test2(void)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")
__flag(BPF_F_ANY_ALIGNMENT)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)
reg_0_reg_xor_1(void)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)
reg32_0_reg32_xor_1(void)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)
reg_2_reg_xor_3(void)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")
reg_any_reg_xor_3(void)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")
reg32_any_reg32_xor_3(void)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)
reg_0_reg_xor_3(void)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)
reg32_0_reg32_xor_3(void)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 check for non const xor src dst")
890 __success __log_level(2)
891 __msg("5: (af) r0 ^= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))")
non_const_xor_src_dst(void)892 __naked void non_const_xor_src_dst(void)
893 {
894 asm volatile (" \
895 call %[bpf_get_prandom_u32]; \
896 r6 = r0; \
897 call %[bpf_get_prandom_u32]; \
898 r6 &= 0xaf; \
899 r0 &= 0x1a0; \
900 r0 ^= r6; \
901 exit; \
902 " :
903 : __imm(bpf_map_lookup_elem),
904 __imm_addr(map_hash_8b),
905 __imm(bpf_get_prandom_u32)
906 : __clobber_all);
907 }
908
909 SEC("socket")
910 __description("bounds check for non const or src dst")
911 __success __log_level(2)
912 __msg("5: (4f) r0 |= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=431,var_off=(0x0; 0x1af))")
non_const_or_src_dst(void)913 __naked void non_const_or_src_dst(void)
914 {
915 asm volatile (" \
916 call %[bpf_get_prandom_u32]; \
917 r6 = r0; \
918 call %[bpf_get_prandom_u32]; \
919 r6 &= 0xaf; \
920 r0 &= 0x1a0; \
921 r0 |= r6; \
922 exit; \
923 " :
924 : __imm(bpf_map_lookup_elem),
925 __imm_addr(map_hash_8b),
926 __imm(bpf_get_prandom_u32)
927 : __clobber_all);
928 }
929
930 SEC("socket")
931 __description("bounds check for non const mul regs")
932 __success __log_level(2)
933 __msg("5: (2f) r0 *= r6 ; R0_w=scalar(smin=smin32=0,smax=umax=smax32=umax32=3825,var_off=(0x0; 0xfff))")
non_const_mul_regs(void)934 __naked void non_const_mul_regs(void)
935 {
936 asm volatile (" \
937 call %[bpf_get_prandom_u32]; \
938 r6 = r0; \
939 call %[bpf_get_prandom_u32]; \
940 r6 &= 0xff; \
941 r0 &= 0x0f; \
942 r0 *= r6; \
943 exit; \
944 " :
945 : __imm(bpf_map_lookup_elem),
946 __imm_addr(map_hash_8b),
947 __imm(bpf_get_prandom_u32)
948 : __clobber_all);
949 }
950
951 SEC("socket")
952 __description("bounds checks after 32-bit truncation. test 1")
953 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
954 __retval(0)
_32_bit_truncation_test_1(void)955 __naked void _32_bit_truncation_test_1(void)
956 {
957 asm volatile (" \
958 r1 = 0; \
959 *(u64*)(r10 - 8) = r1; \
960 r2 = r10; \
961 r2 += -8; \
962 r1 = %[map_hash_8b] ll; \
963 call %[bpf_map_lookup_elem]; \
964 if r0 == 0 goto l0_%=; \
965 r1 = *(u32*)(r0 + 0); \
966 /* This used to reduce the max bound to 0x7fffffff */\
967 if r1 == 0 goto l1_%=; \
968 if r1 > 0x7fffffff goto l0_%=; \
969 l1_%=: r0 = 0; \
970 l0_%=: exit; \
971 " :
972 : __imm(bpf_map_lookup_elem),
973 __imm_addr(map_hash_8b)
974 : __clobber_all);
975 }
976
977 SEC("socket")
978 __description("bounds checks after 32-bit truncation. test 2")
979 __success __failure_unpriv __msg_unpriv("R0 leaks addr")
980 __retval(0)
_32_bit_truncation_test_2(void)981 __naked void _32_bit_truncation_test_2(void)
982 {
983 asm volatile (" \
984 r1 = 0; \
985 *(u64*)(r10 - 8) = r1; \
986 r2 = r10; \
987 r2 += -8; \
988 r1 = %[map_hash_8b] ll; \
989 call %[bpf_map_lookup_elem]; \
990 if r0 == 0 goto l0_%=; \
991 r1 = *(u32*)(r0 + 0); \
992 if r1 s< 1 goto l1_%=; \
993 if w1 s< 0 goto l0_%=; \
994 l1_%=: r0 = 0; \
995 l0_%=: exit; \
996 " :
997 : __imm(bpf_map_lookup_elem),
998 __imm_addr(map_hash_8b)
999 : __clobber_all);
1000 }
1001
1002 SEC("xdp")
1003 __description("bound check with JMP_JLT for crossing 64-bit signed boundary")
1004 __success __retval(0)
crossing_64_bit_signed_boundary_1(void)1005 __naked void crossing_64_bit_signed_boundary_1(void)
1006 {
1007 asm volatile (" \
1008 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1009 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1010 r1 = r2; \
1011 r1 += 1; \
1012 if r1 > r3 goto l0_%=; \
1013 r1 = *(u8*)(r2 + 0); \
1014 r0 = 0x7fffffffffffff10 ll; \
1015 r1 += r0; \
1016 r0 = 0x8000000000000000 ll; \
1017 l1_%=: r0 += 1; \
1018 /* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\
1019 if r0 < r1 goto l1_%=; \
1020 l0_%=: r0 = 0; \
1021 exit; \
1022 " :
1023 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1024 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1025 : __clobber_all);
1026 }
1027
1028 SEC("xdp")
1029 __description("bound check with JMP_JSLT for crossing 64-bit signed boundary")
1030 __success __retval(0)
1031 __flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */
crossing_64_bit_signed_boundary_2(void)1032 __naked void crossing_64_bit_signed_boundary_2(void)
1033 {
1034 asm volatile (" \
1035 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1036 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1037 r1 = r2; \
1038 r1 += 1; \
1039 if r1 > r3 goto l0_%=; \
1040 r1 = *(u8*)(r2 + 0); \
1041 r0 = 0x7fffffffffffff10 ll; \
1042 r1 += r0; \
1043 r2 = 0x8000000000000fff ll; \
1044 r0 = 0x8000000000000000 ll; \
1045 l1_%=: r0 += 1; \
1046 if r0 s> r2 goto l0_%=; \
1047 /* r1 signed range is [S64_MIN, S64_MAX] */ \
1048 if r0 s< r1 goto l1_%=; \
1049 r0 = 1; \
1050 exit; \
1051 l0_%=: r0 = 0; \
1052 exit; \
1053 " :
1054 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1055 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1056 : __clobber_all);
1057 }
1058
1059 SEC("xdp")
1060 __description("bound check for loop upper bound greater than U32_MAX")
1061 __success __retval(0)
bound_greater_than_u32_max(void)1062 __naked void bound_greater_than_u32_max(void)
1063 {
1064 asm volatile (" \
1065 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1066 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1067 r1 = r2; \
1068 r1 += 1; \
1069 if r1 > r3 goto l0_%=; \
1070 r1 = *(u8*)(r2 + 0); \
1071 r0 = 0x100000000 ll; \
1072 r1 += r0; \
1073 r0 = 0x100000000 ll; \
1074 l1_%=: r0 += 1; \
1075 if r0 < r1 goto l1_%=; \
1076 l0_%=: r0 = 0; \
1077 exit; \
1078 " :
1079 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1080 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1081 : __clobber_all);
1082 }
1083
1084 SEC("xdp")
1085 __description("bound check with JMP32_JLT for crossing 32-bit signed boundary")
1086 __success __retval(0)
crossing_32_bit_signed_boundary_1(void)1087 __naked void crossing_32_bit_signed_boundary_1(void)
1088 {
1089 asm volatile (" \
1090 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1091 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1092 r1 = r2; \
1093 r1 += 1; \
1094 if r1 > r3 goto l0_%=; \
1095 r1 = *(u8*)(r2 + 0); \
1096 w0 = 0x7fffff10; \
1097 w1 += w0; \
1098 w0 = 0x80000000; \
1099 l1_%=: w0 += 1; \
1100 /* r1 unsigned range is [0, 0x8000000f] */ \
1101 if w0 < w1 goto l1_%=; \
1102 l0_%=: r0 = 0; \
1103 exit; \
1104 " :
1105 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1106 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1107 : __clobber_all);
1108 }
1109
1110 SEC("xdp")
1111 __description("bound check with JMP32_JSLT for crossing 32-bit signed boundary")
1112 __success __retval(0)
1113 __flag(!BPF_F_TEST_REG_INVARIANTS) /* known invariants violation */
crossing_32_bit_signed_boundary_2(void)1114 __naked void crossing_32_bit_signed_boundary_2(void)
1115 {
1116 asm volatile (" \
1117 r2 = *(u32*)(r1 + %[xdp_md_data]); \
1118 r3 = *(u32*)(r1 + %[xdp_md_data_end]); \
1119 r1 = r2; \
1120 r1 += 1; \
1121 if r1 > r3 goto l0_%=; \
1122 r1 = *(u8*)(r2 + 0); \
1123 w0 = 0x7fffff10; \
1124 w1 += w0; \
1125 w2 = 0x80000fff; \
1126 w0 = 0x80000000; \
1127 l1_%=: w0 += 1; \
1128 if w0 s> w2 goto l0_%=; \
1129 /* r1 signed range is [S32_MIN, S32_MAX] */ \
1130 if w0 s< w1 goto l1_%=; \
1131 r0 = 1; \
1132 exit; \
1133 l0_%=: r0 = 0; \
1134 exit; \
1135 " :
1136 : __imm_const(xdp_md_data, offsetof(struct xdp_md, data)),
1137 __imm_const(xdp_md_data_end, offsetof(struct xdp_md, data_end))
1138 : __clobber_all);
1139 }
1140
1141 SEC("tc")
1142 __description("bounds check with JMP_NE for reg edge")
1143 __success __retval(0)
reg_not_equal_const(void)1144 __naked void reg_not_equal_const(void)
1145 {
1146 asm volatile (" \
1147 r6 = r1; \
1148 r1 = 0; \
1149 *(u64*)(r10 - 8) = r1; \
1150 call %[bpf_get_prandom_u32]; \
1151 r4 = r0; \
1152 r4 &= 7; \
1153 if r4 != 0 goto l0_%=; \
1154 r0 = 0; \
1155 exit; \
1156 l0_%=: r1 = r6; \
1157 r2 = 0; \
1158 r3 = r10; \
1159 r3 += -8; \
1160 r5 = 0; \
1161 /* The 4th argument of bpf_skb_store_bytes is defined as \
1162 * ARG_CONST_SIZE, so 0 is not allowed. The 'r4 != 0' \
1163 * is providing us this exclusion of zero from initial \
1164 * [0, 7] range. \
1165 */ \
1166 call %[bpf_skb_store_bytes]; \
1167 r0 = 0; \
1168 exit; \
1169 " :
1170 : __imm(bpf_get_prandom_u32),
1171 __imm(bpf_skb_store_bytes)
1172 : __clobber_all);
1173 }
1174
1175 SEC("tc")
1176 __description("bounds check with JMP_EQ for reg edge")
1177 __success __retval(0)
reg_equal_const(void)1178 __naked void reg_equal_const(void)
1179 {
1180 asm volatile (" \
1181 r6 = r1; \
1182 r1 = 0; \
1183 *(u64*)(r10 - 8) = r1; \
1184 call %[bpf_get_prandom_u32]; \
1185 r4 = r0; \
1186 r4 &= 7; \
1187 if r4 == 0 goto l0_%=; \
1188 r1 = r6; \
1189 r2 = 0; \
1190 r3 = r10; \
1191 r3 += -8; \
1192 r5 = 0; \
1193 /* Just the same as what we do in reg_not_equal_const() */ \
1194 call %[bpf_skb_store_bytes]; \
1195 l0_%=: r0 = 0; \
1196 exit; \
1197 " :
1198 : __imm(bpf_get_prandom_u32),
1199 __imm(bpf_skb_store_bytes)
1200 : __clobber_all);
1201 }
1202
1203 char _license[] SEC("license") = "GPL";
1204