1// SPDX-License-Identifier: GPL-2.0
2/* Converted from tools/testing/selftests/bpf/verifier/array_access.c */
3
4#include <linux/bpf.h>
5#include <bpf/bpf_helpers.h>
6#include "bpf_misc.h"
7
8#define MAX_ENTRIES 11
9
10struct test_val {
11	unsigned int index;
12	int foo[MAX_ENTRIES];
13};
14
15struct {
16	__uint(type, BPF_MAP_TYPE_ARRAY);
17	__uint(max_entries, 1);
18	__type(key, int);
19	__type(value, struct test_val);
20	__uint(map_flags, BPF_F_RDONLY_PROG);
21} map_array_ro SEC(".maps");
22
23struct {
24	__uint(type, BPF_MAP_TYPE_ARRAY);
25	__uint(max_entries, 1);
26	__type(key, int);
27	__type(value, struct test_val);
28	__uint(map_flags, BPF_F_WRONLY_PROG);
29} map_array_wo SEC(".maps");
30
31struct {
32	__uint(type, BPF_MAP_TYPE_HASH);
33	__uint(max_entries, 1);
34	__type(key, long long);
35	__type(value, struct test_val);
36} map_hash_48b SEC(".maps");
37
38SEC("socket")
39__description("valid map access into an array with a constant")
40__success __failure_unpriv __msg_unpriv("R0 leaks addr")
41__retval(0)
42__naked void an_array_with_a_constant_1(void)
43{
44	asm volatile ("					\
45	r1 = 0;						\
46	*(u64*)(r10 - 8) = r1;				\
47	r2 = r10;					\
48	r2 += -8;					\
49	r1 = %[map_hash_48b] ll;			\
50	call %[bpf_map_lookup_elem];			\
51	if r0 == 0 goto l0_%=;				\
52	r1 = %[test_val_foo];				\
53	*(u64*)(r0 + 0) = r1;				\
54l0_%=:	exit;						\
55"	:
56	: __imm(bpf_map_lookup_elem),
57	  __imm_addr(map_hash_48b),
58	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
59	: __clobber_all);
60}
61
62SEC("socket")
63__description("valid map access into an array with a register")
64__success __failure_unpriv __msg_unpriv("R0 leaks addr")
65__retval(0) __flag(BPF_F_ANY_ALIGNMENT)
66__naked void an_array_with_a_register_1(void)
67{
68	asm volatile ("					\
69	r1 = 0;						\
70	*(u64*)(r10 - 8) = r1;				\
71	r2 = r10;					\
72	r2 += -8;					\
73	r1 = %[map_hash_48b] ll;			\
74	call %[bpf_map_lookup_elem];			\
75	if r0 == 0 goto l0_%=;				\
76	r1 = 4;						\
77	r1 <<= 2;					\
78	r0 += r1;					\
79	r1 = %[test_val_foo];				\
80	*(u64*)(r0 + 0) = r1;				\
81l0_%=:	exit;						\
82"	:
83	: __imm(bpf_map_lookup_elem),
84	  __imm_addr(map_hash_48b),
85	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
86	: __clobber_all);
87}
88
89SEC("socket")
90__description("valid map access into an array with a variable")
91__success __failure_unpriv __msg_unpriv("R0 leaks addr")
92__retval(0) __flag(BPF_F_ANY_ALIGNMENT)
93__naked void an_array_with_a_variable_1(void)
94{
95	asm volatile ("					\
96	r1 = 0;						\
97	*(u64*)(r10 - 8) = r1;				\
98	r2 = r10;					\
99	r2 += -8;					\
100	r1 = %[map_hash_48b] ll;			\
101	call %[bpf_map_lookup_elem];			\
102	if r0 == 0 goto l0_%=;				\
103	r1 = *(u32*)(r0 + 0);				\
104	if r1 >= %[max_entries] goto l0_%=;		\
105	r1 <<= 2;					\
106	r0 += r1;					\
107	r1 = %[test_val_foo];				\
108	*(u64*)(r0 + 0) = r1;				\
109l0_%=:	exit;						\
110"	:
111	: __imm(bpf_map_lookup_elem),
112	  __imm_addr(map_hash_48b),
113	  __imm_const(max_entries, MAX_ENTRIES),
114	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
115	: __clobber_all);
116}
117
118SEC("socket")
119__description("valid map access into an array with a signed variable")
120__success __failure_unpriv __msg_unpriv("R0 leaks addr")
121__retval(0) __flag(BPF_F_ANY_ALIGNMENT)
122__naked void array_with_a_signed_variable(void)
123{
124	asm volatile ("					\
125	r1 = 0;						\
126	*(u64*)(r10 - 8) = r1;				\
127	r2 = r10;					\
128	r2 += -8;					\
129	r1 = %[map_hash_48b] ll;			\
130	call %[bpf_map_lookup_elem];			\
131	if r0 == 0 goto l0_%=;				\
132	r1 = *(u32*)(r0 + 0);				\
133	if w1 s> 0xffffffff goto l1_%=;			\
134	w1 = 0;						\
135l1_%=:	w2 = %[max_entries];				\
136	if r2 s> r1 goto l2_%=;				\
137	w1 = 0;						\
138l2_%=:	w1 <<= 2;					\
139	r0 += r1;					\
140	r1 = %[test_val_foo];				\
141	*(u64*)(r0 + 0) = r1;				\
142l0_%=:	exit;						\
143"	:
144	: __imm(bpf_map_lookup_elem),
145	  __imm_addr(map_hash_48b),
146	  __imm_const(max_entries, MAX_ENTRIES),
147	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
148	: __clobber_all);
149}
150
151SEC("socket")
152__description("invalid map access into an array with a constant")
153__failure __msg("invalid access to map value, value_size=48 off=48 size=8")
154__failure_unpriv
155__naked void an_array_with_a_constant_2(void)
156{
157	asm volatile ("					\
158	r1 = 0;						\
159	*(u64*)(r10 - 8) = r1;				\
160	r2 = r10;					\
161	r2 += -8;					\
162	r1 = %[map_hash_48b] ll;			\
163	call %[bpf_map_lookup_elem];			\
164	if r0 == 0 goto l0_%=;				\
165	r1 = %[test_val_foo];				\
166	*(u64*)(r0 + %[__imm_0]) = r1;			\
167l0_%=:	exit;						\
168"	:
169	: __imm(bpf_map_lookup_elem),
170	  __imm_addr(map_hash_48b),
171	  __imm_const(__imm_0, (MAX_ENTRIES + 1) << 2),
172	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
173	: __clobber_all);
174}
175
176SEC("socket")
177__description("invalid map access into an array with a register")
178__failure __msg("R0 min value is outside of the allowed memory range")
179__failure_unpriv
180__flag(BPF_F_ANY_ALIGNMENT)
181__naked void an_array_with_a_register_2(void)
182{
183	asm volatile ("					\
184	r1 = 0;						\
185	*(u64*)(r10 - 8) = r1;				\
186	r2 = r10;					\
187	r2 += -8;					\
188	r1 = %[map_hash_48b] ll;			\
189	call %[bpf_map_lookup_elem];			\
190	if r0 == 0 goto l0_%=;				\
191	r1 = %[__imm_0];				\
192	r1 <<= 2;					\
193	r0 += r1;					\
194	r1 = %[test_val_foo];				\
195	*(u64*)(r0 + 0) = r1;				\
196l0_%=:	exit;						\
197"	:
198	: __imm(bpf_map_lookup_elem),
199	  __imm_addr(map_hash_48b),
200	  __imm_const(__imm_0, MAX_ENTRIES + 1),
201	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
202	: __clobber_all);
203}
204
205SEC("socket")
206__description("invalid map access into an array with a variable")
207__failure
208__msg("R0 unbounded memory access, make sure to bounds check any such access")
209__failure_unpriv
210__flag(BPF_F_ANY_ALIGNMENT)
211__naked void an_array_with_a_variable_2(void)
212{
213	asm volatile ("					\
214	r1 = 0;						\
215	*(u64*)(r10 - 8) = r1;				\
216	r2 = r10;					\
217	r2 += -8;					\
218	r1 = %[map_hash_48b] ll;			\
219	call %[bpf_map_lookup_elem];			\
220	if r0 == 0 goto l0_%=;				\
221	r1 = *(u32*)(r0 + 0);				\
222	r1 <<= 2;					\
223	r0 += r1;					\
224	r1 = %[test_val_foo];				\
225	*(u64*)(r0 + 0) = r1;				\
226l0_%=:	exit;						\
227"	:
228	: __imm(bpf_map_lookup_elem),
229	  __imm_addr(map_hash_48b),
230	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
231	: __clobber_all);
232}
233
234SEC("socket")
235__description("invalid map access into an array with no floor check")
236__failure __msg("R0 unbounded memory access")
237__failure_unpriv __msg_unpriv("R0 leaks addr")
238__flag(BPF_F_ANY_ALIGNMENT)
239__naked void array_with_no_floor_check(void)
240{
241	asm volatile ("					\
242	r1 = 0;						\
243	*(u64*)(r10 - 8) = r1;				\
244	r2 = r10;					\
245	r2 += -8;					\
246	r1 = %[map_hash_48b] ll;			\
247	call %[bpf_map_lookup_elem];			\
248	if r0 == 0 goto l0_%=;				\
249	r1 = *(u64*)(r0 + 0);				\
250	w2 = %[max_entries];				\
251	if r2 s> r1 goto l1_%=;				\
252	w1 = 0;						\
253l1_%=:	w1 <<= 2;					\
254	r0 += r1;					\
255	r1 = %[test_val_foo];				\
256	*(u64*)(r0 + 0) = r1;				\
257l0_%=:	exit;						\
258"	:
259	: __imm(bpf_map_lookup_elem),
260	  __imm_addr(map_hash_48b),
261	  __imm_const(max_entries, MAX_ENTRIES),
262	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
263	: __clobber_all);
264}
265
266SEC("socket")
267__description("invalid map access into an array with a invalid max check")
268__failure __msg("invalid access to map value, value_size=48 off=44 size=8")
269__failure_unpriv __msg_unpriv("R0 leaks addr")
270__flag(BPF_F_ANY_ALIGNMENT)
271__naked void with_a_invalid_max_check_1(void)
272{
273	asm volatile ("					\
274	r1 = 0;						\
275	*(u64*)(r10 - 8) = r1;				\
276	r2 = r10;					\
277	r2 += -8;					\
278	r1 = %[map_hash_48b] ll;			\
279	call %[bpf_map_lookup_elem];			\
280	if r0 == 0 goto l0_%=;				\
281	r1 = *(u32*)(r0 + 0);				\
282	w2 = %[__imm_0];				\
283	if r2 > r1 goto l1_%=;				\
284	w1 = 0;						\
285l1_%=:	w1 <<= 2;					\
286	r0 += r1;					\
287	r1 = %[test_val_foo];				\
288	*(u64*)(r0 + 0) = r1;				\
289l0_%=:	exit;						\
290"	:
291	: __imm(bpf_map_lookup_elem),
292	  __imm_addr(map_hash_48b),
293	  __imm_const(__imm_0, MAX_ENTRIES + 1),
294	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
295	: __clobber_all);
296}
297
298SEC("socket")
299__description("invalid map access into an array with a invalid max check")
300__failure __msg("R0 pointer += pointer")
301__failure_unpriv
302__flag(BPF_F_ANY_ALIGNMENT)
303__naked void with_a_invalid_max_check_2(void)
304{
305	asm volatile ("					\
306	r1 = 0;						\
307	*(u64*)(r10 - 8) = r1;				\
308	r2 = r10;					\
309	r2 += -8;					\
310	r1 = %[map_hash_48b] ll;			\
311	call %[bpf_map_lookup_elem];			\
312	if r0 == 0 goto l0_%=;				\
313	r8 = r0;					\
314	r1 = 0;						\
315	*(u64*)(r10 - 8) = r1;				\
316	r2 = r10;					\
317	r2 += -8;					\
318	r1 = %[map_hash_48b] ll;			\
319	call %[bpf_map_lookup_elem];			\
320	if r0 == 0 goto l0_%=;				\
321	r0 += r8;					\
322	r0 = *(u32*)(r0 + %[test_val_foo]);		\
323l0_%=:	exit;						\
324"	:
325	: __imm(bpf_map_lookup_elem),
326	  __imm_addr(map_hash_48b),
327	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
328	: __clobber_all);
329}
330
331SEC("socket")
332__description("valid read map access into a read-only array 1")
333__success __success_unpriv __retval(28)
334__naked void a_read_only_array_1_1(void)
335{
336	asm volatile ("					\
337	r1 = 0;						\
338	*(u64*)(r10 - 8) = r1;				\
339	r2 = r10;					\
340	r2 += -8;					\
341	r1 = %[map_array_ro] ll;			\
342	call %[bpf_map_lookup_elem];			\
343	if r0 == 0 goto l0_%=;				\
344	r0 = *(u32*)(r0 + 0);				\
345l0_%=:	exit;						\
346"	:
347	: __imm(bpf_map_lookup_elem),
348	  __imm_addr(map_array_ro)
349	: __clobber_all);
350}
351
352SEC("tc")
353__description("valid read map access into a read-only array 2")
354__success __retval(65507)
355__naked void a_read_only_array_2_1(void)
356{
357	asm volatile ("					\
358	r1 = 0;						\
359	*(u64*)(r10 - 8) = r1;				\
360	r2 = r10;					\
361	r2 += -8;					\
362	r1 = %[map_array_ro] ll;			\
363	call %[bpf_map_lookup_elem];			\
364	if r0 == 0 goto l0_%=;				\
365	r1 = r0;					\
366	r2 = 4;						\
367	r3 = 0;						\
368	r4 = 0;						\
369	r5 = 0;						\
370	call %[bpf_csum_diff];				\
371l0_%=:	r0 &= 0xffff;					\
372	exit;						\
373"	:
374	: __imm(bpf_csum_diff),
375	  __imm(bpf_map_lookup_elem),
376	  __imm_addr(map_array_ro)
377	: __clobber_all);
378}
379
380SEC("socket")
381__description("invalid write map access into a read-only array 1")
382__failure __msg("write into map forbidden")
383__failure_unpriv
384__naked void a_read_only_array_1_2(void)
385{
386	asm volatile ("					\
387	r1 = 0;						\
388	*(u64*)(r10 - 8) = r1;				\
389	r2 = r10;					\
390	r2 += -8;					\
391	r1 = %[map_array_ro] ll;			\
392	call %[bpf_map_lookup_elem];			\
393	if r0 == 0 goto l0_%=;				\
394	r1 = 42;					\
395	*(u64*)(r0 + 0) = r1;				\
396l0_%=:	exit;						\
397"	:
398	: __imm(bpf_map_lookup_elem),
399	  __imm_addr(map_array_ro)
400	: __clobber_all);
401}
402
403SEC("tc")
404__description("invalid write map access into a read-only array 2")
405__failure __msg("write into map forbidden")
406__naked void a_read_only_array_2_2(void)
407{
408	asm volatile ("					\
409	r6 = r1;					\
410	r1 = 0;						\
411	*(u64*)(r10 - 8) = r1;				\
412	r2 = r10;					\
413	r2 += -8;					\
414	r1 = %[map_array_ro] ll;			\
415	call %[bpf_map_lookup_elem];			\
416	if r0 == 0 goto l0_%=;				\
417	r1 = r6;					\
418	r2 = 0;						\
419	r3 = r0;					\
420	r4 = 8;						\
421	call %[bpf_skb_load_bytes];			\
422l0_%=:	exit;						\
423"	:
424	: __imm(bpf_map_lookup_elem),
425	  __imm(bpf_skb_load_bytes),
426	  __imm_addr(map_array_ro)
427	: __clobber_all);
428}
429
430SEC("socket")
431__description("valid write map access into a write-only array 1")
432__success __success_unpriv __retval(1)
433__naked void a_write_only_array_1_1(void)
434{
435	asm volatile ("					\
436	r1 = 0;						\
437	*(u64*)(r10 - 8) = r1;				\
438	r2 = r10;					\
439	r2 += -8;					\
440	r1 = %[map_array_wo] ll;			\
441	call %[bpf_map_lookup_elem];			\
442	if r0 == 0 goto l0_%=;				\
443	r1 = 42;					\
444	*(u64*)(r0 + 0) = r1;				\
445l0_%=:	r0 = 1;						\
446	exit;						\
447"	:
448	: __imm(bpf_map_lookup_elem),
449	  __imm_addr(map_array_wo)
450	: __clobber_all);
451}
452
453SEC("tc")
454__description("valid write map access into a write-only array 2")
455__success __retval(0)
456__naked void a_write_only_array_2_1(void)
457{
458	asm volatile ("					\
459	r6 = r1;					\
460	r1 = 0;						\
461	*(u64*)(r10 - 8) = r1;				\
462	r2 = r10;					\
463	r2 += -8;					\
464	r1 = %[map_array_wo] ll;			\
465	call %[bpf_map_lookup_elem];			\
466	if r0 == 0 goto l0_%=;				\
467	r1 = r6;					\
468	r2 = 0;						\
469	r3 = r0;					\
470	r4 = 8;						\
471	call %[bpf_skb_load_bytes];			\
472l0_%=:	exit;						\
473"	:
474	: __imm(bpf_map_lookup_elem),
475	  __imm(bpf_skb_load_bytes),
476	  __imm_addr(map_array_wo)
477	: __clobber_all);
478}
479
480SEC("socket")
481__description("invalid read map access into a write-only array 1")
482__failure __msg("read from map forbidden")
483__failure_unpriv
484__naked void a_write_only_array_1_2(void)
485{
486	asm volatile ("					\
487	r1 = 0;						\
488	*(u64*)(r10 - 8) = r1;				\
489	r2 = r10;					\
490	r2 += -8;					\
491	r1 = %[map_array_wo] ll;			\
492	call %[bpf_map_lookup_elem];			\
493	if r0 == 0 goto l0_%=;				\
494	r0 = *(u64*)(r0 + 0);				\
495l0_%=:	exit;						\
496"	:
497	: __imm(bpf_map_lookup_elem),
498	  __imm_addr(map_array_wo)
499	: __clobber_all);
500}
501
502SEC("tc")
503__description("invalid read map access into a write-only array 2")
504__failure __msg("read from map forbidden")
505__naked void a_write_only_array_2_2(void)
506{
507	asm volatile ("					\
508	r1 = 0;						\
509	*(u64*)(r10 - 8) = r1;				\
510	r2 = r10;					\
511	r2 += -8;					\
512	r1 = %[map_array_wo] ll;			\
513	call %[bpf_map_lookup_elem];			\
514	if r0 == 0 goto l0_%=;				\
515	r1 = r0;					\
516	r2 = 4;						\
517	r3 = 0;						\
518	r4 = 0;						\
519	r5 = 0;						\
520	call %[bpf_csum_diff];				\
521l0_%=:	exit;						\
522"	:
523	: __imm(bpf_csum_diff),
524	  __imm(bpf_map_lookup_elem),
525	  __imm_addr(map_array_wo)
526	: __clobber_all);
527}
528
529char _license[] SEC("license") = "GPL";
530