1// SPDX-License-Identifier: GPL-2.0
2/* Converted from tools/testing/selftests/bpf/verifier/regalloc.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_HASH);
17	__uint(max_entries, 1);
18	__type(key, long long);
19	__type(value, struct test_val);
20} map_hash_48b SEC(".maps");
21
22SEC("tracepoint")
23__description("regalloc basic")
24__success __flag(BPF_F_ANY_ALIGNMENT)
25__naked void regalloc_basic(void)
26{
27	asm volatile ("					\
28	r6 = r1;					\
29	r1 = 0;						\
30	*(u64*)(r10 - 8) = r1;				\
31	r2 = r10;					\
32	r2 += -8;					\
33	r1 = %[map_hash_48b] ll;			\
34	call %[bpf_map_lookup_elem];			\
35	if r0 == 0 goto l0_%=;				\
36	r7 = r0;					\
37	call %[bpf_get_prandom_u32];			\
38	r2 = r0;					\
39	if r0 s> 20 goto l0_%=;				\
40	if r2 s< 0 goto l0_%=;				\
41	r7 += r0;					\
42	r7 += r2;					\
43	r0 = *(u64*)(r7 + 0);				\
44l0_%=:	exit;						\
45"	:
46	: __imm(bpf_get_prandom_u32),
47	  __imm(bpf_map_lookup_elem),
48	  __imm_addr(map_hash_48b)
49	: __clobber_all);
50}
51
52SEC("tracepoint")
53__description("regalloc negative")
54__failure __msg("invalid access to map value, value_size=48 off=48 size=1")
55__naked void regalloc_negative(void)
56{
57	asm volatile ("					\
58	r6 = r1;					\
59	r1 = 0;						\
60	*(u64*)(r10 - 8) = r1;				\
61	r2 = r10;					\
62	r2 += -8;					\
63	r1 = %[map_hash_48b] ll;			\
64	call %[bpf_map_lookup_elem];			\
65	if r0 == 0 goto l0_%=;				\
66	r7 = r0;					\
67	call %[bpf_get_prandom_u32];			\
68	r2 = r0;					\
69	if r0 s> 24 goto l0_%=;				\
70	if r2 s< 0 goto l0_%=;				\
71	r7 += r0;					\
72	r7 += r2;					\
73	r0 = *(u8*)(r7 + 0);				\
74l0_%=:	exit;						\
75"	:
76	: __imm(bpf_get_prandom_u32),
77	  __imm(bpf_map_lookup_elem),
78	  __imm_addr(map_hash_48b)
79	: __clobber_all);
80}
81
82SEC("tracepoint")
83__description("regalloc src_reg mark")
84__success __flag(BPF_F_ANY_ALIGNMENT)
85__naked void regalloc_src_reg_mark(void)
86{
87	asm volatile ("					\
88	r6 = r1;					\
89	r1 = 0;						\
90	*(u64*)(r10 - 8) = r1;				\
91	r2 = r10;					\
92	r2 += -8;					\
93	r1 = %[map_hash_48b] ll;			\
94	call %[bpf_map_lookup_elem];			\
95	if r0 == 0 goto l0_%=;				\
96	r7 = r0;					\
97	call %[bpf_get_prandom_u32];			\
98	r2 = r0;					\
99	if r0 s> 20 goto l0_%=;				\
100	r3 = 0;						\
101	if r3 s>= r2 goto l0_%=;			\
102	r7 += r0;					\
103	r7 += r2;					\
104	r0 = *(u64*)(r7 + 0);				\
105l0_%=:	exit;						\
106"	:
107	: __imm(bpf_get_prandom_u32),
108	  __imm(bpf_map_lookup_elem),
109	  __imm_addr(map_hash_48b)
110	: __clobber_all);
111}
112
113SEC("tracepoint")
114__description("regalloc src_reg negative")
115__failure __msg("invalid access to map value, value_size=48 off=44 size=8")
116__flag(BPF_F_ANY_ALIGNMENT)
117__naked void regalloc_src_reg_negative(void)
118{
119	asm volatile ("					\
120	r6 = r1;					\
121	r1 = 0;						\
122	*(u64*)(r10 - 8) = r1;				\
123	r2 = r10;					\
124	r2 += -8;					\
125	r1 = %[map_hash_48b] ll;			\
126	call %[bpf_map_lookup_elem];			\
127	if r0 == 0 goto l0_%=;				\
128	r7 = r0;					\
129	call %[bpf_get_prandom_u32];			\
130	r2 = r0;					\
131	if r0 s> 22 goto l0_%=;				\
132	r3 = 0;						\
133	if r3 s>= r2 goto l0_%=;			\
134	r7 += r0;					\
135	r7 += r2;					\
136	r0 = *(u64*)(r7 + 0);				\
137l0_%=:	exit;						\
138"	:
139	: __imm(bpf_get_prandom_u32),
140	  __imm(bpf_map_lookup_elem),
141	  __imm_addr(map_hash_48b)
142	: __clobber_all);
143}
144
145SEC("tracepoint")
146__description("regalloc and spill")
147__success __flag(BPF_F_ANY_ALIGNMENT)
148__naked void regalloc_and_spill(void)
149{
150	asm volatile ("					\
151	r6 = r1;					\
152	r1 = 0;						\
153	*(u64*)(r10 - 8) = r1;				\
154	r2 = r10;					\
155	r2 += -8;					\
156	r1 = %[map_hash_48b] ll;			\
157	call %[bpf_map_lookup_elem];			\
158	if r0 == 0 goto l0_%=;				\
159	r7 = r0;					\
160	call %[bpf_get_prandom_u32];			\
161	r2 = r0;					\
162	if r0 s> 20 goto l0_%=;				\
163	/* r0 has upper bound that should propagate into r2 */\
164	*(u64*)(r10 - 8) = r2;		/* spill r2 */	\
165	r0 = 0;						\
166	r2 = 0;				/* clear r0 and r2 */\
167	r3 = *(u64*)(r10 - 8);		/* fill r3 */	\
168	if r0 s>= r3 goto l0_%=;			\
169	/* r3 has lower and upper bounds */		\
170	r7 += r3;					\
171	r0 = *(u64*)(r7 + 0);				\
172l0_%=:	exit;						\
173"	:
174	: __imm(bpf_get_prandom_u32),
175	  __imm(bpf_map_lookup_elem),
176	  __imm_addr(map_hash_48b)
177	: __clobber_all);
178}
179
180SEC("tracepoint")
181__description("regalloc and spill negative")
182__failure __msg("invalid access to map value, value_size=48 off=48 size=8")
183__flag(BPF_F_ANY_ALIGNMENT)
184__naked void regalloc_and_spill_negative(void)
185{
186	asm volatile ("					\
187	r6 = r1;					\
188	r1 = 0;						\
189	*(u64*)(r10 - 8) = r1;				\
190	r2 = r10;					\
191	r2 += -8;					\
192	r1 = %[map_hash_48b] ll;			\
193	call %[bpf_map_lookup_elem];			\
194	if r0 == 0 goto l0_%=;				\
195	r7 = r0;					\
196	call %[bpf_get_prandom_u32];			\
197	r2 = r0;					\
198	if r0 s> 48 goto l0_%=;				\
199	/* r0 has upper bound that should propagate into r2 */\
200	*(u64*)(r10 - 8) = r2;		/* spill r2 */	\
201	r0 = 0;						\
202	r2 = 0;				/* clear r0 and r2 */\
203	r3 = *(u64*)(r10 - 8);		/* fill r3 */\
204	if r0 s>= r3 goto l0_%=;			\
205	/* r3 has lower and upper bounds */		\
206	r7 += r3;					\
207	r0 = *(u64*)(r7 + 0);				\
208l0_%=:	exit;						\
209"	:
210	: __imm(bpf_get_prandom_u32),
211	  __imm(bpf_map_lookup_elem),
212	  __imm_addr(map_hash_48b)
213	: __clobber_all);
214}
215
216SEC("tracepoint")
217__description("regalloc three regs")
218__success __flag(BPF_F_ANY_ALIGNMENT)
219__naked void regalloc_three_regs(void)
220{
221	asm volatile ("					\
222	r6 = r1;					\
223	r1 = 0;						\
224	*(u64*)(r10 - 8) = r1;				\
225	r2 = r10;					\
226	r2 += -8;					\
227	r1 = %[map_hash_48b] ll;			\
228	call %[bpf_map_lookup_elem];			\
229	if r0 == 0 goto l0_%=;				\
230	r7 = r0;					\
231	call %[bpf_get_prandom_u32];			\
232	r2 = r0;					\
233	r4 = r2;					\
234	if r0 s> 12 goto l0_%=;				\
235	if r2 s< 0 goto l0_%=;				\
236	r7 += r0;					\
237	r7 += r2;					\
238	r7 += r4;					\
239	r0 = *(u64*)(r7 + 0);				\
240l0_%=:	exit;						\
241"	:
242	: __imm(bpf_get_prandom_u32),
243	  __imm(bpf_map_lookup_elem),
244	  __imm_addr(map_hash_48b)
245	: __clobber_all);
246}
247
248SEC("tracepoint")
249__description("regalloc after call")
250__success __flag(BPF_F_ANY_ALIGNMENT)
251__naked void regalloc_after_call(void)
252{
253	asm volatile ("					\
254	r6 = r1;					\
255	r1 = 0;						\
256	*(u64*)(r10 - 8) = r1;				\
257	r2 = r10;					\
258	r2 += -8;					\
259	r1 = %[map_hash_48b] ll;			\
260	call %[bpf_map_lookup_elem];			\
261	if r0 == 0 goto l0_%=;				\
262	r7 = r0;					\
263	call %[bpf_get_prandom_u32];			\
264	r8 = r0;					\
265	r9 = r0;					\
266	call regalloc_after_call__1;			\
267	if r8 s> 20 goto l0_%=;				\
268	if r9 s< 0 goto l0_%=;				\
269	r7 += r8;					\
270	r7 += r9;					\
271	r0 = *(u64*)(r7 + 0);				\
272l0_%=:	exit;						\
273"	:
274	: __imm(bpf_get_prandom_u32),
275	  __imm(bpf_map_lookup_elem),
276	  __imm_addr(map_hash_48b)
277	: __clobber_all);
278}
279
280static __naked __noinline __attribute__((used))
281void regalloc_after_call__1(void)
282{
283	asm volatile ("					\
284	r0 = 0;						\
285	exit;						\
286"	::: __clobber_all);
287}
288
289SEC("tracepoint")
290__description("regalloc in callee")
291__success __flag(BPF_F_ANY_ALIGNMENT)
292__naked void regalloc_in_callee(void)
293{
294	asm volatile ("					\
295	r6 = r1;					\
296	r1 = 0;						\
297	*(u64*)(r10 - 8) = r1;				\
298	r2 = r10;					\
299	r2 += -8;					\
300	r1 = %[map_hash_48b] ll;			\
301	call %[bpf_map_lookup_elem];			\
302	if r0 == 0 goto l0_%=;				\
303	r7 = r0;					\
304	call %[bpf_get_prandom_u32];			\
305	r1 = r0;					\
306	r2 = r0;					\
307	r3 = r7;					\
308	call regalloc_in_callee__1;			\
309l0_%=:	exit;						\
310"	:
311	: __imm(bpf_get_prandom_u32),
312	  __imm(bpf_map_lookup_elem),
313	  __imm_addr(map_hash_48b)
314	: __clobber_all);
315}
316
317static __naked __noinline __attribute__((used))
318void regalloc_in_callee__1(void)
319{
320	asm volatile ("					\
321	if r1 s> 20 goto l0_%=;				\
322	if r2 s< 0 goto l0_%=;				\
323	r3 += r1;					\
324	r3 += r2;					\
325	r0 = *(u64*)(r3 + 0);				\
326	exit;						\
327l0_%=:	r0 = 0;						\
328	exit;						\
329"	::: __clobber_all);
330}
331
332SEC("tracepoint")
333__description("regalloc, spill, JEQ")
334__success
335__naked void regalloc_spill_jeq(void)
336{
337	asm volatile ("					\
338	r6 = r1;					\
339	r1 = 0;						\
340	*(u64*)(r10 - 8) = r1;				\
341	r2 = r10;					\
342	r2 += -8;					\
343	r1 = %[map_hash_48b] ll;			\
344	call %[bpf_map_lookup_elem];			\
345	*(u64*)(r10 - 8) = r0;		/* spill r0 */	\
346	if r0 == 0 goto l0_%=;				\
347l0_%=:	/* The verifier will walk the rest twice with r0 == 0 and r0 == map_value */\
348	call %[bpf_get_prandom_u32];			\
349	r2 = r0;					\
350	if r2 == 20 goto l1_%=;				\
351l1_%=:	/* The verifier will walk the rest two more times with r0 == 20 and r0 == unknown */\
352	r3 = *(u64*)(r10 - 8);		/* fill r3 with map_value */\
353	if r3 == 0 goto l2_%=;		/* skip ldx if map_value == NULL */\
354	/* Buggy verifier will think that r3 == 20 here */\
355	r0 = *(u64*)(r3 + 0);		/* read from map_value */\
356l2_%=:	exit;						\
357"	:
358	: __imm(bpf_get_prandom_u32),
359	  __imm(bpf_map_lookup_elem),
360	  __imm_addr(map_hash_48b)
361	: __clobber_all);
362}
363
364char _license[] SEC("license") = "GPL";
365