1226633Sdim// SPDX-License-Identifier: GPL-2.0
2193326Sed/* Converted from tools/testing/selftests/bpf/verifier/array_access.c */
3193326Sed
4193326Sed#include <linux/bpf.h>
5193326Sed#include <bpf/bpf_helpers.h>
6193326Sed#include "bpf_misc.h"
7193326Sed
8193326Sed#define MAX_ENTRIES 11
9193326Sed
10193326Sedstruct test_val {
11193326Sed	unsigned int index;
12193326Sed	int foo[MAX_ENTRIES];
13193326Sed};
14193326Sed
15193326Sedstruct {
16193326Sed	__uint(type, BPF_MAP_TYPE_ARRAY);
17249423Sdim	__uint(max_entries, 1);
18226633Sdim	__type(key, int);
19226633Sdim	__type(value, struct test_val);
20226633Sdim	__uint(map_flags, BPF_F_RDONLY_PROG);
21193326Sed} map_array_ro SEC(".maps");
22193326Sed
23193326Sedstruct {
24226633Sdim	__uint(type, BPF_MAP_TYPE_ARRAY);
25226633Sdim	__uint(max_entries, 1);
26193326Sed	__type(key, int);
27193326Sed	__type(value, struct test_val);
28193326Sed	__uint(map_flags, BPF_F_WRONLY_PROG);
29226633Sdim} map_array_wo SEC(".maps");
30226633Sdim
31226633Sdimstruct {
32226633Sdim	__uint(type, BPF_MAP_TYPE_HASH);
33226633Sdim	__uint(max_entries, 1);
34198092Srdivacky	__type(key, long long);
35226633Sdim	__type(value, struct test_val);
36226633Sdim} map_hash_48b SEC(".maps");
37226633Sdim
38226633SdimSEC("socket")
39193326Sed__description("valid map access into an array with a constant")
40226633Sdim__success __failure_unpriv __msg_unpriv("R0 leaks addr")
41226633Sdim__retval(0)
42193326Sed__naked void an_array_with_a_constant_1(void)
43226633Sdim{
44226633Sdim	asm volatile ("					\
45226633Sdim	r1 = 0;						\
46193326Sed	*(u64*)(r10 - 8) = r1;				\
47226633Sdim	r2 = r10;					\
48226633Sdim	r2 += -8;					\
49226633Sdim	r1 = %[map_hash_48b] ll;			\
50226633Sdim	call %[bpf_map_lookup_elem];			\
51226633Sdim	if r0 == 0 goto l0_%=;				\
52226633Sdim	r1 = %[test_val_foo];				\
53193326Sed	*(u64*)(r0 + 0) = r1;				\
54226633Sdiml0_%=:	exit;						\
55234353Sdim"	:
56234353Sdim	: __imm(bpf_map_lookup_elem),
57234353Sdim	  __imm_addr(map_hash_48b),
58226633Sdim	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
59226633Sdim	: __clobber_all);
60226633Sdim}
61193326Sed
62226633SdimSEC("socket")
63226633Sdim__description("valid map access into an array with a register")
64226633Sdim__success __failure_unpriv __msg_unpriv("R0 leaks addr")
65226633Sdim__retval(0) __flag(BPF_F_ANY_ALIGNMENT)
66226633Sdim__naked void an_array_with_a_register_1(void)
67226633Sdim{
68226633Sdim	asm volatile ("					\
69226633Sdim	r1 = 0;						\
70198092Srdivacky	*(u64*)(r10 - 8) = r1;				\
71193326Sed	r2 = r10;					\
72226633Sdim	r2 += -8;					\
73226633Sdim	r1 = %[map_hash_48b] ll;			\
74226633Sdim	call %[bpf_map_lookup_elem];			\
75234353Sdim	if r0 == 0 goto l0_%=;				\
76226633Sdim	r1 = 4;						\
77226633Sdim	r1 <<= 2;					\
78226633Sdim	r0 += r1;					\
79193326Sed	r1 = %[test_val_foo];				\
80226633Sdim	*(u64*)(r0 + 0) = r1;				\
81226633Sdiml0_%=:	exit;						\
82226633Sdim"	:
83193326Sed	: __imm(bpf_map_lookup_elem),
84193326Sed	  __imm_addr(map_hash_48b),
85193326Sed	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
86226633Sdim	: __clobber_all);
87226633Sdim}
88226633Sdim
89193326SedSEC("socket")
90226633Sdim__description("valid map access into an array with a variable")
91226633Sdim__success __failure_unpriv __msg_unpriv("R0 leaks addr")
92226633Sdim__retval(0) __flag(BPF_F_ANY_ALIGNMENT)
93226633Sdim__naked void an_array_with_a_variable_1(void)
94226633Sdim{
95198092Srdivacky	asm volatile ("					\
96226633Sdim	r1 = 0;						\
97226633Sdim	*(u64*)(r10 - 8) = r1;				\
98234353Sdim	r2 = r10;					\
99226633Sdim	r2 += -8;					\
100226633Sdim	r1 = %[map_hash_48b] ll;			\
101226633Sdim	call %[bpf_map_lookup_elem];			\
102226633Sdim	if r0 == 0 goto l0_%=;				\
103226633Sdim	r1 = *(u32*)(r0 + 0);				\
104226633Sdim	if r1 >= %[max_entries] goto l0_%=;		\
105226633Sdim	r1 <<= 2;					\
106226633Sdim	r0 += r1;					\
107193326Sed	r1 = %[test_val_foo];				\
108226633Sdim	*(u64*)(r0 + 0) = r1;				\
109226633Sdiml0_%=:	exit;						\
110226633Sdim"	:
111234353Sdim	: __imm(bpf_map_lookup_elem),
112226633Sdim	  __imm_addr(map_hash_48b),
113226633Sdim	  __imm_const(max_entries, MAX_ENTRIES),
114226633Sdim	  __imm_const(test_val_foo, offsetof(struct test_val, foo))
115226633Sdim	: __clobber_all);
116226633Sdim}
117226633Sdim
118193326SedSEC("socket")
119193326Sed__description("valid map access into an array with a signed variable")
120193326Sed__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