xref: /linux/tools/testing/selftests/bpf/progs/verifier_regalloc.c (revision 2a6b6c9a226279b4f6668450ddb21ae655558087)
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 
10 struct test_val {
11 	unsigned int index;
12 	int foo[MAX_ENTRIES];
13 };
14 
15 struct {
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 
22 SEC("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);				\
44 l0_%=:	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 
52 SEC("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);				\
74 l0_%=:	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 
82 SEC("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);				\
105 l0_%=:	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 
113 SEC("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);				\
137 l0_%=:	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 
145 SEC("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);				\
172 l0_%=:	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 
180 SEC("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);				\
208 l0_%=:	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 
216 SEC("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);				\
240 l0_%=:	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 
248 SEC("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);				\
272 l0_%=:	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 
280 static __naked __noinline __attribute__((used))
281 void regalloc_after_call__1(void)
282 {
283 	asm volatile ("					\
284 	r0 = 0;						\
285 	exit;						\
286 "	::: __clobber_all);
287 }
288 
289 SEC("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;			\
309 l0_%=:	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 
317 static __naked __noinline __attribute__((used))
318 void 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;						\
327 l0_%=:	r0 = 0;						\
328 	exit;						\
329 "	::: __clobber_all);
330 }
331 
332 SEC("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_%=;				\
347 l0_%=:	/* 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_%=;				\
351 l1_%=:	/* 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 */\
356 l2_%=:	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 
364 char _license[] SEC("license") = "GPL";
365