1// SPDX-License-Identifier: GPL-2.0
2/* Converted from tools/testing/selftests/bpf/verifier/helper_value_access.c */
3
4#include <linux/bpf.h>
5#include <bpf/bpf_helpers.h>
6#include "bpf_misc.h"
7
8struct other_val {
9	long long foo;
10	long long bar;
11};
12
13struct {
14	__uint(type, BPF_MAP_TYPE_HASH);
15	__uint(max_entries, 1);
16	__type(key, long long);
17	__type(value, struct other_val);
18} map_hash_16b SEC(".maps");
19
20#define MAX_ENTRIES 11
21
22struct test_val {
23	unsigned int index;
24	int foo[MAX_ENTRIES];
25};
26
27struct {
28	__uint(type, BPF_MAP_TYPE_HASH);
29	__uint(max_entries, 1);
30	__type(key, long long);
31	__type(value, struct test_val);
32} map_hash_48b SEC(".maps");
33
34struct {
35	__uint(type, BPF_MAP_TYPE_HASH);
36	__uint(max_entries, 1);
37	__type(key, long long);
38	__type(value, long long);
39} map_hash_8b SEC(".maps");
40
41SEC("tracepoint")
42__description("helper access to map: full range")
43__success
44__naked void access_to_map_full_range(void)
45{
46	asm volatile ("					\
47	r2 = r10;					\
48	r2 += -8;					\
49	r1 = 0;						\
50	*(u64*)(r2 + 0) = r1;				\
51	r1 = %[map_hash_48b] ll;			\
52	call %[bpf_map_lookup_elem];			\
53	if r0 == 0 goto l0_%=;				\
54	r1 = r0;					\
55	r2 = %[sizeof_test_val];			\
56	r3 = 0;						\
57	call %[bpf_probe_read_kernel];			\
58l0_%=:	exit;						\
59"	:
60	: __imm(bpf_map_lookup_elem),
61	  __imm(bpf_probe_read_kernel),
62	  __imm_addr(map_hash_48b),
63	  __imm_const(sizeof_test_val, sizeof(struct test_val))
64	: __clobber_all);
65}
66
67SEC("tracepoint")
68__description("helper access to map: partial range")
69__success
70__naked void access_to_map_partial_range(void)
71{
72	asm volatile ("					\
73	r2 = r10;					\
74	r2 += -8;					\
75	r1 = 0;						\
76	*(u64*)(r2 + 0) = r1;				\
77	r1 = %[map_hash_48b] ll;			\
78	call %[bpf_map_lookup_elem];			\
79	if r0 == 0 goto l0_%=;				\
80	r1 = r0;					\
81	r2 = 8;						\
82	r3 = 0;						\
83	call %[bpf_probe_read_kernel];			\
84l0_%=:	exit;						\
85"	:
86	: __imm(bpf_map_lookup_elem),
87	  __imm(bpf_probe_read_kernel),
88	  __imm_addr(map_hash_48b)
89	: __clobber_all);
90}
91
92/* Call a function taking a pointer and a size which doesn't allow the size to
93 * be zero (i.e. bpf_trace_printk() declares the second argument to be
94 * ARG_CONST_SIZE, not ARG_CONST_SIZE_OR_ZERO). We attempt to pass zero for the
95 * size and expect to fail.
96 */
97SEC("tracepoint")
98__description("helper access to map: empty range")
99__failure __msg("R2 invalid zero-sized read: u64=[0,0]")
100__naked void access_to_map_empty_range(void)
101{
102	asm volatile ("					\
103	r2 = r10;					\
104	r2 += -8;					\
105	r1 = 0;						\
106	*(u64*)(r2 + 0) = r1;				\
107	r1 = %[map_hash_48b] ll;			\
108	call %[bpf_map_lookup_elem];			\
109	if r0 == 0 goto l0_%=;				\
110	r1 = r0;					\
111	r2 = 0;						\
112	call %[bpf_trace_printk];			\
113l0_%=:	exit;						\
114"	:
115	: __imm(bpf_map_lookup_elem),
116	  __imm(bpf_trace_printk),
117	  __imm_addr(map_hash_48b)
118	: __clobber_all);
119}
120
121/* Like the test above, but this time the size register is not known to be zero;
122 * its lower-bound is zero though, which is still unacceptable.
123 */
124SEC("tracepoint")
125__description("helper access to map: possibly-empty ange")
126__failure __msg("R2 invalid zero-sized read: u64=[0,4]")
127__naked void access_to_map_possibly_empty_range(void)
128{
129	asm volatile ("                                         \
130	r2 = r10;                                               \
131	r2 += -8;                                               \
132	r1 = 0;                                                 \
133	*(u64*)(r2 + 0) = r1;                                   \
134	r1 = %[map_hash_48b] ll;                                \
135	call %[bpf_map_lookup_elem];                            \
136	if r0 == 0 goto l0_%=;                                  \
137	r1 = r0;                                                \
138	/* Read an unknown value */                             \
139	r7 = *(u64*)(r0 + 0);                                   \
140	/* Make it small and positive, to avoid other errors */ \
141	r7 &= 4;                                                \
142	r2 = 0;                                                 \
143	r2 += r7;                                               \
144	call %[bpf_trace_printk];                               \
145l0_%=:	exit;                                               \
146"	:
147	: __imm(bpf_map_lookup_elem),
148	  __imm(bpf_trace_printk),
149	  __imm_addr(map_hash_48b)
150	: __clobber_all);
151}
152
153SEC("tracepoint")
154__description("helper access to map: out-of-bound range")
155__failure __msg("invalid access to map value, value_size=48 off=0 size=56")
156__naked void map_out_of_bound_range(void)
157{
158	asm volatile ("					\
159	r2 = r10;					\
160	r2 += -8;					\
161	r1 = 0;						\
162	*(u64*)(r2 + 0) = r1;				\
163	r1 = %[map_hash_48b] ll;			\
164	call %[bpf_map_lookup_elem];			\
165	if r0 == 0 goto l0_%=;				\
166	r1 = r0;					\
167	r2 = %[__imm_0];				\
168	r3 = 0;						\
169	call %[bpf_probe_read_kernel];			\
170l0_%=:	exit;						\
171"	:
172	: __imm(bpf_map_lookup_elem),
173	  __imm(bpf_probe_read_kernel),
174	  __imm_addr(map_hash_48b),
175	  __imm_const(__imm_0, sizeof(struct test_val) + 8)
176	: __clobber_all);
177}
178
179SEC("tracepoint")
180__description("helper access to map: negative range")
181__failure __msg("R2 min value is negative")
182__naked void access_to_map_negative_range(void)
183{
184	asm volatile ("					\
185	r2 = r10;					\
186	r2 += -8;					\
187	r1 = 0;						\
188	*(u64*)(r2 + 0) = r1;				\
189	r1 = %[map_hash_48b] ll;			\
190	call %[bpf_map_lookup_elem];			\
191	if r0 == 0 goto l0_%=;				\
192	r1 = r0;					\
193	r2 = -8;					\
194	r3 = 0;						\
195	call %[bpf_probe_read_kernel];			\
196l0_%=:	exit;						\
197"	:
198	: __imm(bpf_map_lookup_elem),
199	  __imm(bpf_probe_read_kernel),
200	  __imm_addr(map_hash_48b)
201	: __clobber_all);
202}
203
204SEC("tracepoint")
205__description("helper access to adjusted map (via const imm): full range")
206__success
207__naked void via_const_imm_full_range(void)
208{
209	asm volatile ("					\
210	r2 = r10;					\
211	r2 += -8;					\
212	r1 = 0;						\
213	*(u64*)(r2 + 0) = r1;				\
214	r1 = %[map_hash_48b] ll;			\
215	call %[bpf_map_lookup_elem];			\
216	if r0 == 0 goto l0_%=;				\
217	r1 = r0;					\
218	r1 += %[test_val_foo];				\
219	r2 = %[__imm_0];				\
220	r3 = 0;						\
221	call %[bpf_probe_read_kernel];			\
222l0_%=:	exit;						\
223"	:
224	: __imm(bpf_map_lookup_elem),
225	  __imm(bpf_probe_read_kernel),
226	  __imm_addr(map_hash_48b),
227	  __imm_const(__imm_0, sizeof(struct test_val) - offsetof(struct test_val, foo)),
228	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
229	: __clobber_all);
230}
231
232SEC("tracepoint")
233__description("helper access to adjusted map (via const imm): partial range")
234__success
235__naked void via_const_imm_partial_range(void)
236{
237	asm volatile ("					\
238	r2 = r10;					\
239	r2 += -8;					\
240	r1 = 0;						\
241	*(u64*)(r2 + 0) = r1;				\
242	r1 = %[map_hash_48b] ll;			\
243	call %[bpf_map_lookup_elem];			\
244	if r0 == 0 goto l0_%=;				\
245	r1 = r0;					\
246	r1 += %[test_val_foo];				\
247	r2 = 8;						\
248	r3 = 0;						\
249	call %[bpf_probe_read_kernel];			\
250l0_%=:	exit;						\
251"	:
252	: __imm(bpf_map_lookup_elem),
253	  __imm(bpf_probe_read_kernel),
254	  __imm_addr(map_hash_48b),
255	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
256	: __clobber_all);
257}
258
259SEC("tracepoint")
260__description("helper access to adjusted map (via const imm): empty range")
261__failure __msg("R2 invalid zero-sized read")
262__naked void via_const_imm_empty_range(void)
263{
264	asm volatile ("					\
265	r2 = r10;					\
266	r2 += -8;					\
267	r1 = 0;						\
268	*(u64*)(r2 + 0) = r1;				\
269	r1 = %[map_hash_48b] ll;			\
270	call %[bpf_map_lookup_elem];			\
271	if r0 == 0 goto l0_%=;				\
272	r1 = r0;					\
273	r1 += %[test_val_foo];				\
274	r2 = 0;						\
275	call %[bpf_trace_printk];			\
276l0_%=:	exit;						\
277"	:
278	: __imm(bpf_map_lookup_elem),
279	  __imm(bpf_trace_printk),
280	  __imm_addr(map_hash_48b),
281	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
282	: __clobber_all);
283}
284
285SEC("tracepoint")
286__description("helper access to adjusted map (via const imm): out-of-bound range")
287__failure __msg("invalid access to map value, value_size=48 off=4 size=52")
288__naked void imm_out_of_bound_range(void)
289{
290	asm volatile ("					\
291	r2 = r10;					\
292	r2 += -8;					\
293	r1 = 0;						\
294	*(u64*)(r2 + 0) = r1;				\
295	r1 = %[map_hash_48b] ll;			\
296	call %[bpf_map_lookup_elem];			\
297	if r0 == 0 goto l0_%=;				\
298	r1 = r0;					\
299	r1 += %[test_val_foo];				\
300	r2 = %[__imm_0];				\
301	r3 = 0;						\
302	call %[bpf_probe_read_kernel];			\
303l0_%=:	exit;						\
304"	:
305	: __imm(bpf_map_lookup_elem),
306	  __imm(bpf_probe_read_kernel),
307	  __imm_addr(map_hash_48b),
308	  __imm_const(__imm_0, sizeof(struct test_val) - offsetof(struct test_val, foo) + 8),
309	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
310	: __clobber_all);
311}
312
313SEC("tracepoint")
314__description("helper access to adjusted map (via const imm): negative range (> adjustment)")
315__failure __msg("R2 min value is negative")
316__naked void const_imm_negative_range_adjustment_1(void)
317{
318	asm volatile ("					\
319	r2 = r10;					\
320	r2 += -8;					\
321	r1 = 0;						\
322	*(u64*)(r2 + 0) = r1;				\
323	r1 = %[map_hash_48b] ll;			\
324	call %[bpf_map_lookup_elem];			\
325	if r0 == 0 goto l0_%=;				\
326	r1 = r0;					\
327	r1 += %[test_val_foo];				\
328	r2 = -8;					\
329	r3 = 0;						\
330	call %[bpf_probe_read_kernel];			\
331l0_%=:	exit;						\
332"	:
333	: __imm(bpf_map_lookup_elem),
334	  __imm(bpf_probe_read_kernel),
335	  __imm_addr(map_hash_48b),
336	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
337	: __clobber_all);
338}
339
340SEC("tracepoint")
341__description("helper access to adjusted map (via const imm): negative range (< adjustment)")
342__failure __msg("R2 min value is negative")
343__naked void const_imm_negative_range_adjustment_2(void)
344{
345	asm volatile ("					\
346	r2 = r10;					\
347	r2 += -8;					\
348	r1 = 0;						\
349	*(u64*)(r2 + 0) = r1;				\
350	r1 = %[map_hash_48b] ll;			\
351	call %[bpf_map_lookup_elem];			\
352	if r0 == 0 goto l0_%=;				\
353	r1 = r0;					\
354	r1 += %[test_val_foo];				\
355	r2 = -1;					\
356	r3 = 0;						\
357	call %[bpf_probe_read_kernel];			\
358l0_%=:	exit;						\
359"	:
360	: __imm(bpf_map_lookup_elem),
361	  __imm(bpf_probe_read_kernel),
362	  __imm_addr(map_hash_48b),
363	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
364	: __clobber_all);
365}
366
367SEC("tracepoint")
368__description("helper access to adjusted map (via const reg): full range")
369__success
370__naked void via_const_reg_full_range(void)
371{
372	asm volatile ("					\
373	r2 = r10;					\
374	r2 += -8;					\
375	r1 = 0;						\
376	*(u64*)(r2 + 0) = r1;				\
377	r1 = %[map_hash_48b] ll;			\
378	call %[bpf_map_lookup_elem];			\
379	if r0 == 0 goto l0_%=;				\
380	r1 = r0;					\
381	r3 = %[test_val_foo];				\
382	r1 += r3;					\
383	r2 = %[__imm_0];				\
384	r3 = 0;						\
385	call %[bpf_probe_read_kernel];			\
386l0_%=:	exit;						\
387"	:
388	: __imm(bpf_map_lookup_elem),
389	  __imm(bpf_probe_read_kernel),
390	  __imm_addr(map_hash_48b),
391	  __imm_const(__imm_0, sizeof(struct test_val) - offsetof(struct test_val, foo)),
392	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
393	: __clobber_all);
394}
395
396SEC("tracepoint")
397__description("helper access to adjusted map (via const reg): partial range")
398__success
399__naked void via_const_reg_partial_range(void)
400{
401	asm volatile ("					\
402	r2 = r10;					\
403	r2 += -8;					\
404	r1 = 0;						\
405	*(u64*)(r2 + 0) = r1;				\
406	r1 = %[map_hash_48b] ll;			\
407	call %[bpf_map_lookup_elem];			\
408	if r0 == 0 goto l0_%=;				\
409	r1 = r0;					\
410	r3 = %[test_val_foo];				\
411	r1 += r3;					\
412	r2 = 8;						\
413	r3 = 0;						\
414	call %[bpf_probe_read_kernel];			\
415l0_%=:	exit;						\
416"	:
417	: __imm(bpf_map_lookup_elem),
418	  __imm(bpf_probe_read_kernel),
419	  __imm_addr(map_hash_48b),
420	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
421	: __clobber_all);
422}
423
424SEC("tracepoint")
425__description("helper access to adjusted map (via const reg): empty range")
426__failure __msg("R2 invalid zero-sized read")
427__naked void via_const_reg_empty_range(void)
428{
429	asm volatile ("					\
430	r2 = r10;					\
431	r2 += -8;					\
432	r1 = 0;						\
433	*(u64*)(r2 + 0) = r1;				\
434	r1 = %[map_hash_48b] ll;			\
435	call %[bpf_map_lookup_elem];			\
436	if r0 == 0 goto l0_%=;				\
437	r1 = r0;					\
438	r3 = 0;						\
439	r1 += r3;					\
440	r2 = 0;						\
441	call %[bpf_trace_printk];			\
442l0_%=:	exit;						\
443"	:
444	: __imm(bpf_map_lookup_elem),
445	  __imm(bpf_trace_printk),
446	  __imm_addr(map_hash_48b)
447	: __clobber_all);
448}
449
450SEC("tracepoint")
451__description("helper access to adjusted map (via const reg): out-of-bound range")
452__failure __msg("invalid access to map value, value_size=48 off=4 size=52")
453__naked void reg_out_of_bound_range(void)
454{
455	asm volatile ("					\
456	r2 = r10;					\
457	r2 += -8;					\
458	r1 = 0;						\
459	*(u64*)(r2 + 0) = r1;				\
460	r1 = %[map_hash_48b] ll;			\
461	call %[bpf_map_lookup_elem];			\
462	if r0 == 0 goto l0_%=;				\
463	r1 = r0;					\
464	r3 = %[test_val_foo];				\
465	r1 += r3;					\
466	r2 = %[__imm_0];				\
467	r3 = 0;						\
468	call %[bpf_probe_read_kernel];			\
469l0_%=:	exit;						\
470"	:
471	: __imm(bpf_map_lookup_elem),
472	  __imm(bpf_probe_read_kernel),
473	  __imm_addr(map_hash_48b),
474	  __imm_const(__imm_0, sizeof(struct test_val) - offsetof(struct test_val, foo) + 8),
475	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
476	: __clobber_all);
477}
478
479SEC("tracepoint")
480__description("helper access to adjusted map (via const reg): negative range (> adjustment)")
481__failure __msg("R2 min value is negative")
482__naked void const_reg_negative_range_adjustment_1(void)
483{
484	asm volatile ("					\
485	r2 = r10;					\
486	r2 += -8;					\
487	r1 = 0;						\
488	*(u64*)(r2 + 0) = r1;				\
489	r1 = %[map_hash_48b] ll;			\
490	call %[bpf_map_lookup_elem];			\
491	if r0 == 0 goto l0_%=;				\
492	r1 = r0;					\
493	r3 = %[test_val_foo];				\
494	r1 += r3;					\
495	r2 = -8;					\
496	r3 = 0;						\
497	call %[bpf_probe_read_kernel];			\
498l0_%=:	exit;						\
499"	:
500	: __imm(bpf_map_lookup_elem),
501	  __imm(bpf_probe_read_kernel),
502	  __imm_addr(map_hash_48b),
503	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
504	: __clobber_all);
505}
506
507SEC("tracepoint")
508__description("helper access to adjusted map (via const reg): negative range (< adjustment)")
509__failure __msg("R2 min value is negative")
510__naked void const_reg_negative_range_adjustment_2(void)
511{
512	asm volatile ("					\
513	r2 = r10;					\
514	r2 += -8;					\
515	r1 = 0;						\
516	*(u64*)(r2 + 0) = r1;				\
517	r1 = %[map_hash_48b] ll;			\
518	call %[bpf_map_lookup_elem];			\
519	if r0 == 0 goto l0_%=;				\
520	r1 = r0;					\
521	r3 = %[test_val_foo];				\
522	r1 += r3;					\
523	r2 = -1;					\
524	r3 = 0;						\
525	call %[bpf_probe_read_kernel];			\
526l0_%=:	exit;						\
527"	:
528	: __imm(bpf_map_lookup_elem),
529	  __imm(bpf_probe_read_kernel),
530	  __imm_addr(map_hash_48b),
531	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
532	: __clobber_all);
533}
534
535SEC("tracepoint")
536__description("helper access to adjusted map (via variable): full range")
537__success
538__naked void map_via_variable_full_range(void)
539{
540	asm volatile ("					\
541	r2 = r10;					\
542	r2 += -8;					\
543	r1 = 0;						\
544	*(u64*)(r2 + 0) = r1;				\
545	r1 = %[map_hash_48b] ll;			\
546	call %[bpf_map_lookup_elem];			\
547	if r0 == 0 goto l0_%=;				\
548	r1 = r0;					\
549	r3 = *(u32*)(r0 + 0);				\
550	if r3 > %[test_val_foo] goto l0_%=;		\
551	r1 += r3;					\
552	r2 = %[__imm_0];				\
553	r3 = 0;						\
554	call %[bpf_probe_read_kernel];			\
555l0_%=:	exit;						\
556"	:
557	: __imm(bpf_map_lookup_elem),
558	  __imm(bpf_probe_read_kernel),
559	  __imm_addr(map_hash_48b),
560	  __imm_const(__imm_0, sizeof(struct test_val) - offsetof(struct test_val, foo)),
561	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
562	: __clobber_all);
563}
564
565SEC("tracepoint")
566__description("helper access to adjusted map (via variable): partial range")
567__success
568__naked void map_via_variable_partial_range(void)
569{
570	asm volatile ("					\
571	r2 = r10;					\
572	r2 += -8;					\
573	r1 = 0;						\
574	*(u64*)(r2 + 0) = r1;				\
575	r1 = %[map_hash_48b] ll;			\
576	call %[bpf_map_lookup_elem];			\
577	if r0 == 0 goto l0_%=;				\
578	r1 = r0;					\
579	r3 = *(u32*)(r0 + 0);				\
580	if r3 > %[test_val_foo] goto l0_%=;		\
581	r1 += r3;					\
582	r2 = 8;						\
583	r3 = 0;						\
584	call %[bpf_probe_read_kernel];			\
585l0_%=:	exit;						\
586"	:
587	: __imm(bpf_map_lookup_elem),
588	  __imm(bpf_probe_read_kernel),
589	  __imm_addr(map_hash_48b),
590	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
591	: __clobber_all);
592}
593
594SEC("tracepoint")
595__description("helper access to adjusted map (via variable): empty range")
596__failure __msg("R2 invalid zero-sized read")
597__naked void map_via_variable_empty_range(void)
598{
599	asm volatile ("					\
600	r2 = r10;					\
601	r2 += -8;					\
602	r1 = 0;						\
603	*(u64*)(r2 + 0) = r1;				\
604	r1 = %[map_hash_48b] ll;			\
605	call %[bpf_map_lookup_elem];			\
606	if r0 == 0 goto l0_%=;				\
607	r1 = r0;					\
608	r3 = *(u32*)(r0 + 0);				\
609	if r3 > %[test_val_foo] goto l0_%=;		\
610	r1 += r3;					\
611	r2 = 0;						\
612	call %[bpf_trace_printk];			\
613l0_%=:	exit;						\
614"	:
615	: __imm(bpf_map_lookup_elem),
616	  __imm(bpf_trace_printk),
617	  __imm_addr(map_hash_48b),
618	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
619	: __clobber_all);
620}
621
622SEC("tracepoint")
623__description("helper access to adjusted map (via variable): no max check")
624__failure __msg("R1 unbounded memory access")
625__naked void via_variable_no_max_check_1(void)
626{
627	asm volatile ("					\
628	r2 = r10;					\
629	r2 += -8;					\
630	r1 = 0;						\
631	*(u64*)(r2 + 0) = r1;				\
632	r1 = %[map_hash_48b] ll;			\
633	call %[bpf_map_lookup_elem];			\
634	if r0 == 0 goto l0_%=;				\
635	r1 = r0;					\
636	r3 = *(u32*)(r0 + 0);				\
637	r1 += r3;					\
638	r2 = 1;						\
639	r3 = 0;						\
640	call %[bpf_probe_read_kernel];			\
641l0_%=:	exit;						\
642"	:
643	: __imm(bpf_map_lookup_elem),
644	  __imm(bpf_probe_read_kernel),
645	  __imm_addr(map_hash_48b)
646	: __clobber_all);
647}
648
649SEC("tracepoint")
650__description("helper access to adjusted map (via variable): wrong max check")
651__failure __msg("invalid access to map value, value_size=48 off=4 size=45")
652__naked void via_variable_wrong_max_check_1(void)
653{
654	asm volatile ("					\
655	r2 = r10;					\
656	r2 += -8;					\
657	r1 = 0;						\
658	*(u64*)(r2 + 0) = r1;				\
659	r1 = %[map_hash_48b] ll;			\
660	call %[bpf_map_lookup_elem];			\
661	if r0 == 0 goto l0_%=;				\
662	r1 = r0;					\
663	r3 = *(u32*)(r0 + 0);				\
664	if r3 > %[test_val_foo] goto l0_%=;		\
665	r1 += r3;					\
666	r2 = %[__imm_0];				\
667	r3 = 0;						\
668	call %[bpf_probe_read_kernel];			\
669l0_%=:	exit;						\
670"	:
671	: __imm(bpf_map_lookup_elem),
672	  __imm(bpf_probe_read_kernel),
673	  __imm_addr(map_hash_48b),
674	  __imm_const(__imm_0, sizeof(struct test_val) - offsetof(struct test_val, foo) + 1),
675	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
676	: __clobber_all);
677}
678
679SEC("tracepoint")
680__description("helper access to map: bounds check using <, good access")
681__success
682__naked void bounds_check_using_good_access_1(void)
683{
684	asm volatile ("					\
685	r2 = r10;					\
686	r2 += -8;					\
687	r1 = 0;						\
688	*(u64*)(r2 + 0) = r1;				\
689	r1 = %[map_hash_48b] ll;			\
690	call %[bpf_map_lookup_elem];			\
691	if r0 == 0 goto l0_%=;				\
692	r1 = r0;					\
693	r3 = *(u32*)(r0 + 0);				\
694	if r3 < 32 goto l1_%=;				\
695	r0 = 0;						\
696l0_%=:	exit;						\
697l1_%=:	r1 += r3;					\
698	r0 = 0;						\
699	*(u8*)(r1 + 0) = r0;				\
700	r0 = 0;						\
701	exit;						\
702"	:
703	: __imm(bpf_map_lookup_elem),
704	  __imm_addr(map_hash_48b)
705	: __clobber_all);
706}
707
708SEC("tracepoint")
709__description("helper access to map: bounds check using <, bad access")
710__failure __msg("R1 unbounded memory access")
711__naked void bounds_check_using_bad_access_1(void)
712{
713	asm volatile ("					\
714	r2 = r10;					\
715	r2 += -8;					\
716	r1 = 0;						\
717	*(u64*)(r2 + 0) = r1;				\
718	r1 = %[map_hash_48b] ll;			\
719	call %[bpf_map_lookup_elem];			\
720	if r0 == 0 goto l0_%=;				\
721	r1 = r0;					\
722	r3 = *(u32*)(r0 + 0);				\
723	if r3 < 32 goto l1_%=;				\
724	r1 += r3;					\
725l0_%=:	r0 = 0;						\
726	*(u8*)(r1 + 0) = r0;				\
727	r0 = 0;						\
728	exit;						\
729l1_%=:	r0 = 0;						\
730	exit;						\
731"	:
732	: __imm(bpf_map_lookup_elem),
733	  __imm_addr(map_hash_48b)
734	: __clobber_all);
735}
736
737SEC("tracepoint")
738__description("helper access to map: bounds check using <=, good access")
739__success
740__naked void bounds_check_using_good_access_2(void)
741{
742	asm volatile ("					\
743	r2 = r10;					\
744	r2 += -8;					\
745	r1 = 0;						\
746	*(u64*)(r2 + 0) = r1;				\
747	r1 = %[map_hash_48b] ll;			\
748	call %[bpf_map_lookup_elem];			\
749	if r0 == 0 goto l0_%=;				\
750	r1 = r0;					\
751	r3 = *(u32*)(r0 + 0);				\
752	if r3 <= 32 goto l1_%=;				\
753	r0 = 0;						\
754l0_%=:	exit;						\
755l1_%=:	r1 += r3;					\
756	r0 = 0;						\
757	*(u8*)(r1 + 0) = r0;				\
758	r0 = 0;						\
759	exit;						\
760"	:
761	: __imm(bpf_map_lookup_elem),
762	  __imm_addr(map_hash_48b)
763	: __clobber_all);
764}
765
766SEC("tracepoint")
767__description("helper access to map: bounds check using <=, bad access")
768__failure __msg("R1 unbounded memory access")
769__naked void bounds_check_using_bad_access_2(void)
770{
771	asm volatile ("					\
772	r2 = r10;					\
773	r2 += -8;					\
774	r1 = 0;						\
775	*(u64*)(r2 + 0) = r1;				\
776	r1 = %[map_hash_48b] ll;			\
777	call %[bpf_map_lookup_elem];			\
778	if r0 == 0 goto l0_%=;				\
779	r1 = r0;					\
780	r3 = *(u32*)(r0 + 0);				\
781	if r3 <= 32 goto l1_%=;				\
782	r1 += r3;					\
783l0_%=:	r0 = 0;						\
784	*(u8*)(r1 + 0) = r0;				\
785	r0 = 0;						\
786	exit;						\
787l1_%=:	r0 = 0;						\
788	exit;						\
789"	:
790	: __imm(bpf_map_lookup_elem),
791	  __imm_addr(map_hash_48b)
792	: __clobber_all);
793}
794
795SEC("tracepoint")
796__description("helper access to map: bounds check using s<, good access")
797__success
798__naked void check_using_s_good_access_1(void)
799{
800	asm volatile ("					\
801	r2 = r10;					\
802	r2 += -8;					\
803	r1 = 0;						\
804	*(u64*)(r2 + 0) = r1;				\
805	r1 = %[map_hash_48b] ll;			\
806	call %[bpf_map_lookup_elem];			\
807	if r0 == 0 goto l0_%=;				\
808	r1 = r0;					\
809	r3 = *(u32*)(r0 + 0);				\
810	if r3 s< 32 goto l1_%=;				\
811l2_%=:	r0 = 0;						\
812l0_%=:	exit;						\
813l1_%=:	if r3 s< 0 goto l2_%=;				\
814	r1 += r3;					\
815	r0 = 0;						\
816	*(u8*)(r1 + 0) = r0;				\
817	r0 = 0;						\
818	exit;						\
819"	:
820	: __imm(bpf_map_lookup_elem),
821	  __imm_addr(map_hash_48b)
822	: __clobber_all);
823}
824
825SEC("tracepoint")
826__description("helper access to map: bounds check using s<, good access 2")
827__success
828__naked void using_s_good_access_2_1(void)
829{
830	asm volatile ("					\
831	r2 = r10;					\
832	r2 += -8;					\
833	r1 = 0;						\
834	*(u64*)(r2 + 0) = r1;				\
835	r1 = %[map_hash_48b] ll;			\
836	call %[bpf_map_lookup_elem];			\
837	if r0 == 0 goto l0_%=;				\
838	r1 = r0;					\
839	r3 = *(u32*)(r0 + 0);				\
840	if r3 s< 32 goto l1_%=;				\
841l2_%=:	r0 = 0;						\
842l0_%=:	exit;						\
843l1_%=:	if r3 s< -3 goto l2_%=;				\
844	r1 += r3;					\
845	r0 = 0;						\
846	*(u8*)(r1 + 0) = r0;				\
847	r0 = 0;						\
848	exit;						\
849"	:
850	: __imm(bpf_map_lookup_elem),
851	  __imm_addr(map_hash_48b)
852	: __clobber_all);
853}
854
855SEC("tracepoint")
856__description("helper access to map: bounds check using s<, bad access")
857__failure __msg("R1 min value is negative")
858__naked void check_using_s_bad_access_1(void)
859{
860	asm volatile ("					\
861	r2 = r10;					\
862	r2 += -8;					\
863	r1 = 0;						\
864	*(u64*)(r2 + 0) = r1;				\
865	r1 = %[map_hash_48b] ll;			\
866	call %[bpf_map_lookup_elem];			\
867	if r0 == 0 goto l0_%=;				\
868	r1 = r0;					\
869	r3 = *(u64*)(r0 + 0);				\
870	if r3 s< 32 goto l1_%=;				\
871l2_%=:	r0 = 0;						\
872l0_%=:	exit;						\
873l1_%=:	if r3 s< -3 goto l2_%=;				\
874	r1 += r3;					\
875	r0 = 0;						\
876	*(u8*)(r1 + 0) = r0;				\
877	r0 = 0;						\
878	exit;						\
879"	:
880	: __imm(bpf_map_lookup_elem),
881	  __imm_addr(map_hash_48b)
882	: __clobber_all);
883}
884
885SEC("tracepoint")
886__description("helper access to map: bounds check using s<=, good access")
887__success
888__naked void check_using_s_good_access_2(void)
889{
890	asm volatile ("					\
891	r2 = r10;					\
892	r2 += -8;					\
893	r1 = 0;						\
894	*(u64*)(r2 + 0) = r1;				\
895	r1 = %[map_hash_48b] ll;			\
896	call %[bpf_map_lookup_elem];			\
897	if r0 == 0 goto l0_%=;				\
898	r1 = r0;					\
899	r3 = *(u32*)(r0 + 0);				\
900	if r3 s<= 32 goto l1_%=;			\
901l2_%=:	r0 = 0;						\
902l0_%=:	exit;						\
903l1_%=:	if r3 s<= 0 goto l2_%=;				\
904	r1 += r3;					\
905	r0 = 0;						\
906	*(u8*)(r1 + 0) = r0;				\
907	r0 = 0;						\
908	exit;						\
909"	:
910	: __imm(bpf_map_lookup_elem),
911	  __imm_addr(map_hash_48b)
912	: __clobber_all);
913}
914
915SEC("tracepoint")
916__description("helper access to map: bounds check using s<=, good access 2")
917__success
918__naked void using_s_good_access_2_2(void)
919{
920	asm volatile ("					\
921	r2 = r10;					\
922	r2 += -8;					\
923	r1 = 0;						\
924	*(u64*)(r2 + 0) = r1;				\
925	r1 = %[map_hash_48b] ll;			\
926	call %[bpf_map_lookup_elem];			\
927	if r0 == 0 goto l0_%=;				\
928	r1 = r0;					\
929	r3 = *(u32*)(r0 + 0);				\
930	if r3 s<= 32 goto l1_%=;			\
931l2_%=:	r0 = 0;						\
932l0_%=:	exit;						\
933l1_%=:	if r3 s<= -3 goto l2_%=;			\
934	r1 += r3;					\
935	r0 = 0;						\
936	*(u8*)(r1 + 0) = r0;				\
937	r0 = 0;						\
938	exit;						\
939"	:
940	: __imm(bpf_map_lookup_elem),
941	  __imm_addr(map_hash_48b)
942	: __clobber_all);
943}
944
945SEC("tracepoint")
946__description("helper access to map: bounds check using s<=, bad access")
947__failure __msg("R1 min value is negative")
948__naked void check_using_s_bad_access_2(void)
949{
950	asm volatile ("					\
951	r2 = r10;					\
952	r2 += -8;					\
953	r1 = 0;						\
954	*(u64*)(r2 + 0) = r1;				\
955	r1 = %[map_hash_48b] ll;			\
956	call %[bpf_map_lookup_elem];			\
957	if r0 == 0 goto l0_%=;				\
958	r1 = r0;					\
959	r3 = *(u64*)(r0 + 0);				\
960	if r3 s<= 32 goto l1_%=;			\
961l2_%=:	r0 = 0;						\
962l0_%=:	exit;						\
963l1_%=:	if r3 s<= -3 goto l2_%=;			\
964	r1 += r3;					\
965	r0 = 0;						\
966	*(u8*)(r1 + 0) = r0;				\
967	r0 = 0;						\
968	exit;						\
969"	:
970	: __imm(bpf_map_lookup_elem),
971	  __imm_addr(map_hash_48b)
972	: __clobber_all);
973}
974
975SEC("tracepoint")
976__description("map lookup helper access to map")
977__success
978__naked void lookup_helper_access_to_map(void)
979{
980	asm volatile ("					\
981	r2 = r10;					\
982	r2 += -8;					\
983	r1 = 0;						\
984	*(u64*)(r2 + 0) = r1;				\
985	r1 = %[map_hash_16b] ll;			\
986	call %[bpf_map_lookup_elem];			\
987	if r0 == 0 goto l0_%=;				\
988	r2 = r0;					\
989	r1 = %[map_hash_16b] ll;			\
990	call %[bpf_map_lookup_elem];			\
991l0_%=:	exit;						\
992"	:
993	: __imm(bpf_map_lookup_elem),
994	  __imm_addr(map_hash_16b)
995	: __clobber_all);
996}
997
998SEC("tracepoint")
999__description("map update helper access to map")
1000__success
1001__naked void update_helper_access_to_map(void)
1002{
1003	asm volatile ("					\
1004	r2 = r10;					\
1005	r2 += -8;					\
1006	r1 = 0;						\
1007	*(u64*)(r2 + 0) = r1;				\
1008	r1 = %[map_hash_16b] ll;			\
1009	call %[bpf_map_lookup_elem];			\
1010	if r0 == 0 goto l0_%=;				\
1011	r4 = 0;						\
1012	r3 = r0;					\
1013	r2 = r0;					\
1014	r1 = %[map_hash_16b] ll;			\
1015	call %[bpf_map_update_elem];			\
1016l0_%=:	exit;						\
1017"	:
1018	: __imm(bpf_map_lookup_elem),
1019	  __imm(bpf_map_update_elem),
1020	  __imm_addr(map_hash_16b)
1021	: __clobber_all);
1022}
1023
1024SEC("tracepoint")
1025__description("map update helper access to map: wrong size")
1026__failure __msg("invalid access to map value, value_size=8 off=0 size=16")
1027__naked void access_to_map_wrong_size(void)
1028{
1029	asm volatile ("					\
1030	r2 = r10;					\
1031	r2 += -8;					\
1032	r1 = 0;						\
1033	*(u64*)(r2 + 0) = r1;				\
1034	r1 = %[map_hash_8b] ll;				\
1035	call %[bpf_map_lookup_elem];			\
1036	if r0 == 0 goto l0_%=;				\
1037	r4 = 0;						\
1038	r3 = r0;					\
1039	r2 = r0;					\
1040	r1 = %[map_hash_16b] ll;			\
1041	call %[bpf_map_update_elem];			\
1042l0_%=:	exit;						\
1043"	:
1044	: __imm(bpf_map_lookup_elem),
1045	  __imm(bpf_map_update_elem),
1046	  __imm_addr(map_hash_16b),
1047	  __imm_addr(map_hash_8b)
1048	: __clobber_all);
1049}
1050
1051SEC("tracepoint")
1052__description("map helper access to adjusted map (via const imm)")
1053__success
1054__naked void adjusted_map_via_const_imm(void)
1055{
1056	asm volatile ("					\
1057	r2 = r10;					\
1058	r2 += -8;					\
1059	r1 = 0;						\
1060	*(u64*)(r2 + 0) = r1;				\
1061	r1 = %[map_hash_16b] ll;			\
1062	call %[bpf_map_lookup_elem];			\
1063	if r0 == 0 goto l0_%=;				\
1064	r2 = r0;					\
1065	r2 += %[other_val_bar];				\
1066	r1 = %[map_hash_16b] ll;			\
1067	call %[bpf_map_lookup_elem];			\
1068l0_%=:	exit;						\
1069"	:
1070	: __imm(bpf_map_lookup_elem),
1071	  __imm_addr(map_hash_16b),
1072	  __imm_const(other_val_bar, offsetof(struct other_val, bar))
1073	: __clobber_all);
1074}
1075
1076SEC("tracepoint")
1077__description("map helper access to adjusted map (via const imm): out-of-bound 1")
1078__failure __msg("invalid access to map value, value_size=16 off=12 size=8")
1079__naked void imm_out_of_bound_1(void)
1080{
1081	asm volatile ("					\
1082	r2 = r10;					\
1083	r2 += -8;					\
1084	r1 = 0;						\
1085	*(u64*)(r2 + 0) = r1;				\
1086	r1 = %[map_hash_16b] ll;			\
1087	call %[bpf_map_lookup_elem];			\
1088	if r0 == 0 goto l0_%=;				\
1089	r2 = r0;					\
1090	r2 += %[__imm_0];				\
1091	r1 = %[map_hash_16b] ll;			\
1092	call %[bpf_map_lookup_elem];			\
1093l0_%=:	exit;						\
1094"	:
1095	: __imm(bpf_map_lookup_elem),
1096	  __imm_addr(map_hash_16b),
1097	  __imm_const(__imm_0, sizeof(struct other_val) - 4)
1098	: __clobber_all);
1099}
1100
1101SEC("tracepoint")
1102__description("map helper access to adjusted map (via const imm): out-of-bound 2")
1103__failure __msg("invalid access to map value, value_size=16 off=-4 size=8")
1104__naked void imm_out_of_bound_2(void)
1105{
1106	asm volatile ("					\
1107	r2 = r10;					\
1108	r2 += -8;					\
1109	r1 = 0;						\
1110	*(u64*)(r2 + 0) = r1;				\
1111	r1 = %[map_hash_16b] ll;			\
1112	call %[bpf_map_lookup_elem];			\
1113	if r0 == 0 goto l0_%=;				\
1114	r2 = r0;					\
1115	r2 += -4;					\
1116	r1 = %[map_hash_16b] ll;			\
1117	call %[bpf_map_lookup_elem];			\
1118l0_%=:	exit;						\
1119"	:
1120	: __imm(bpf_map_lookup_elem),
1121	  __imm_addr(map_hash_16b)
1122	: __clobber_all);
1123}
1124
1125SEC("tracepoint")
1126__description("map helper access to adjusted map (via const reg)")
1127__success
1128__naked void adjusted_map_via_const_reg(void)
1129{
1130	asm volatile ("					\
1131	r2 = r10;					\
1132	r2 += -8;					\
1133	r1 = 0;						\
1134	*(u64*)(r2 + 0) = r1;				\
1135	r1 = %[map_hash_16b] ll;			\
1136	call %[bpf_map_lookup_elem];			\
1137	if r0 == 0 goto l0_%=;				\
1138	r2 = r0;					\
1139	r3 = %[other_val_bar];				\
1140	r2 += r3;					\
1141	r1 = %[map_hash_16b] ll;			\
1142	call %[bpf_map_lookup_elem];			\
1143l0_%=:	exit;						\
1144"	:
1145	: __imm(bpf_map_lookup_elem),
1146	  __imm_addr(map_hash_16b),
1147	  __imm_const(other_val_bar, offsetof(struct other_val, bar))
1148	: __clobber_all);
1149}
1150
1151SEC("tracepoint")
1152__description("map helper access to adjusted map (via const reg): out-of-bound 1")
1153__failure __msg("invalid access to map value, value_size=16 off=12 size=8")
1154__naked void reg_out_of_bound_1(void)
1155{
1156	asm volatile ("					\
1157	r2 = r10;					\
1158	r2 += -8;					\
1159	r1 = 0;						\
1160	*(u64*)(r2 + 0) = r1;				\
1161	r1 = %[map_hash_16b] ll;			\
1162	call %[bpf_map_lookup_elem];			\
1163	if r0 == 0 goto l0_%=;				\
1164	r2 = r0;					\
1165	r3 = %[__imm_0];				\
1166	r2 += r3;					\
1167	r1 = %[map_hash_16b] ll;			\
1168	call %[bpf_map_lookup_elem];			\
1169l0_%=:	exit;						\
1170"	:
1171	: __imm(bpf_map_lookup_elem),
1172	  __imm_addr(map_hash_16b),
1173	  __imm_const(__imm_0, sizeof(struct other_val) - 4)
1174	: __clobber_all);
1175}
1176
1177SEC("tracepoint")
1178__description("map helper access to adjusted map (via const reg): out-of-bound 2")
1179__failure __msg("invalid access to map value, value_size=16 off=-4 size=8")
1180__naked void reg_out_of_bound_2(void)
1181{
1182	asm volatile ("					\
1183	r2 = r10;					\
1184	r2 += -8;					\
1185	r1 = 0;						\
1186	*(u64*)(r2 + 0) = r1;				\
1187	r1 = %[map_hash_16b] ll;			\
1188	call %[bpf_map_lookup_elem];			\
1189	if r0 == 0 goto l0_%=;				\
1190	r2 = r0;					\
1191	r3 = -4;					\
1192	r2 += r3;					\
1193	r1 = %[map_hash_16b] ll;			\
1194	call %[bpf_map_lookup_elem];			\
1195l0_%=:	exit;						\
1196"	:
1197	: __imm(bpf_map_lookup_elem),
1198	  __imm_addr(map_hash_16b)
1199	: __clobber_all);
1200}
1201
1202SEC("tracepoint")
1203__description("map helper access to adjusted map (via variable)")
1204__success
1205__naked void to_adjusted_map_via_variable(void)
1206{
1207	asm volatile ("					\
1208	r2 = r10;					\
1209	r2 += -8;					\
1210	r1 = 0;						\
1211	*(u64*)(r2 + 0) = r1;				\
1212	r1 = %[map_hash_16b] ll;			\
1213	call %[bpf_map_lookup_elem];			\
1214	if r0 == 0 goto l0_%=;				\
1215	r2 = r0;					\
1216	r3 = *(u32*)(r0 + 0);				\
1217	if r3 > %[other_val_bar] goto l0_%=;		\
1218	r2 += r3;					\
1219	r1 = %[map_hash_16b] ll;			\
1220	call %[bpf_map_lookup_elem];			\
1221l0_%=:	exit;						\
1222"	:
1223	: __imm(bpf_map_lookup_elem),
1224	  __imm_addr(map_hash_16b),
1225	  __imm_const(other_val_bar, offsetof(struct other_val, bar))
1226	: __clobber_all);
1227}
1228
1229SEC("tracepoint")
1230__description("map helper access to adjusted map (via variable): no max check")
1231__failure
1232__msg("R2 unbounded memory access, make sure to bounds check any such access")
1233__naked void via_variable_no_max_check_2(void)
1234{
1235	asm volatile ("					\
1236	r2 = r10;					\
1237	r2 += -8;					\
1238	r1 = 0;						\
1239	*(u64*)(r2 + 0) = r1;				\
1240	r1 = %[map_hash_16b] ll;			\
1241	call %[bpf_map_lookup_elem];			\
1242	if r0 == 0 goto l0_%=;				\
1243	r2 = r0;					\
1244	r3 = *(u32*)(r0 + 0);				\
1245	r2 += r3;					\
1246	r1 = %[map_hash_16b] ll;			\
1247	call %[bpf_map_lookup_elem];			\
1248l0_%=:	exit;						\
1249"	:
1250	: __imm(bpf_map_lookup_elem),
1251	  __imm_addr(map_hash_16b)
1252	: __clobber_all);
1253}
1254
1255SEC("tracepoint")
1256__description("map helper access to adjusted map (via variable): wrong max check")
1257__failure __msg("invalid access to map value, value_size=16 off=9 size=8")
1258__naked void via_variable_wrong_max_check_2(void)
1259{
1260	asm volatile ("					\
1261	r2 = r10;					\
1262	r2 += -8;					\
1263	r1 = 0;						\
1264	*(u64*)(r2 + 0) = r1;				\
1265	r1 = %[map_hash_16b] ll;			\
1266	call %[bpf_map_lookup_elem];			\
1267	if r0 == 0 goto l0_%=;				\
1268	r2 = r0;					\
1269	r3 = *(u32*)(r0 + 0);				\
1270	if r3 > %[__imm_0] goto l0_%=;			\
1271	r2 += r3;					\
1272	r1 = %[map_hash_16b] ll;			\
1273	call %[bpf_map_lookup_elem];			\
1274l0_%=:	exit;						\
1275"	:
1276	: __imm(bpf_map_lookup_elem),
1277	  __imm_addr(map_hash_16b),
1278	  __imm_const(__imm_0, offsetof(struct other_val, bar) + 1)
1279	: __clobber_all);
1280}
1281
1282char _license[] SEC("license") = "GPL";
1283