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 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))")
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
909SEC("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))")
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
930SEC("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))")
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
951SEC("socket")
952__description("bounds checks after 32-bit truncation. test 1")
953__success __failure_unpriv __msg_unpriv("R0 leaks addr")
954__retval(0)
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_%=;			\
969l1_%=:	r0 = 0;						\
970l0_%=:	exit;						\
971"	:
972	: __imm(bpf_map_lookup_elem),
973	  __imm_addr(map_hash_8b)
974	: __clobber_all);
975}
976
977SEC("socket")
978__description("bounds checks after 32-bit truncation. test 2")
979__success __failure_unpriv __msg_unpriv("R0 leaks addr")
980__retval(0)
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_%=;				\
994l1_%=:	r0 = 0;						\
995l0_%=:	exit;						\
996"	:
997	: __imm(bpf_map_lookup_elem),
998	  __imm_addr(map_hash_8b)
999	: __clobber_all);
1000}
1001
1002SEC("xdp")
1003__description("bound check with JMP_JLT for crossing 64-bit signed boundary")
1004__success __retval(0)
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;			\
1017l1_%=:	r0 += 1;					\
1018	/* r1 unsigned range is [0x7fffffffffffff10, 0x800000000000000f] */\
1019	if r0 < r1 goto l1_%=;				\
1020l0_%=:	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
1028SEC("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 */
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;			\
1045l1_%=:	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;						\
1051l0_%=:	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
1059SEC("xdp")
1060__description("bound check for loop upper bound greater than U32_MAX")
1061__success __retval(0)
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;				\
1074l1_%=:	r0 += 1;					\
1075	if r0 < r1 goto l1_%=;				\
1076l0_%=:	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
1084SEC("xdp")
1085__description("bound check with JMP32_JLT for crossing 32-bit signed boundary")
1086__success __retval(0)
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;				\
1099l1_%=:	w0 += 1;					\
1100	/* r1 unsigned range is [0, 0x8000000f] */	\
1101	if w0 < w1 goto l1_%=;				\
1102l0_%=:	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
1110SEC("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 */
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;				\
1127l1_%=:	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;						\
1133l0_%=:	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
1141SEC("tc")
1142__description("bounds check with JMP_NE for reg edge")
1143__success __retval(0)
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;						\
1156l0_%=:	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
1175SEC("tc")
1176__description("bounds check with JMP_EQ for reg edge")
1177__success __retval(0)
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];			\
1195l0_%=:	r0 = 0;						\
1196	exit;						\
1197"	:
1198	: __imm(bpf_get_prandom_u32),
1199	  __imm(bpf_skb_store_bytes)
1200	: __clobber_all);
1201}
1202
1203char _license[] SEC("license") = "GPL";
1204