xref: /linux/tools/testing/selftests/bpf/progs/verifier_stack_ptr.c (revision fd7d598270724cc787982ea48bbe17ad383a8b7f)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/stack_ptr.c */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include <limits.h>
7 #include "bpf_misc.h"
8 
9 #define MAX_ENTRIES 11
10 
11 struct test_val {
12 	unsigned int index;
13 	int foo[MAX_ENTRIES];
14 };
15 
16 struct {
17 	__uint(type, BPF_MAP_TYPE_ARRAY);
18 	__uint(max_entries, 1);
19 	__type(key, int);
20 	__type(value, struct test_val);
21 } map_array_48b SEC(".maps");
22 
23 SEC("socket")
24 __description("PTR_TO_STACK store/load")
25 __success __success_unpriv __retval(0xfaceb00c)
26 __naked void ptr_to_stack_store_load(void)
27 {
28 	asm volatile ("					\
29 	r1 = r10;					\
30 	r1 += -10;					\
31 	r0 = 0xfaceb00c;				\
32 	*(u64*)(r1 + 2) = r0;				\
33 	r0 = *(u64*)(r1 + 2);				\
34 	exit;						\
35 "	::: __clobber_all);
36 }
37 
38 SEC("socket")
39 __description("PTR_TO_STACK store/load - bad alignment on off")
40 __failure __msg("misaligned stack access off (0x0; 0x0)+-8+2 size 8")
41 __failure_unpriv
42 __naked void load_bad_alignment_on_off(void)
43 {
44 	asm volatile ("					\
45 	r1 = r10;					\
46 	r1 += -8;					\
47 	r0 = 0xfaceb00c;				\
48 	*(u64*)(r1 + 2) = r0;				\
49 	r0 = *(u64*)(r1 + 2);				\
50 	exit;						\
51 "	::: __clobber_all);
52 }
53 
54 SEC("socket")
55 __description("PTR_TO_STACK store/load - bad alignment on reg")
56 __failure __msg("misaligned stack access off (0x0; 0x0)+-10+8 size 8")
57 __failure_unpriv
58 __naked void load_bad_alignment_on_reg(void)
59 {
60 	asm volatile ("					\
61 	r1 = r10;					\
62 	r1 += -10;					\
63 	r0 = 0xfaceb00c;				\
64 	*(u64*)(r1 + 8) = r0;				\
65 	r0 = *(u64*)(r1 + 8);				\
66 	exit;						\
67 "	::: __clobber_all);
68 }
69 
70 SEC("socket")
71 __description("PTR_TO_STACK store/load - out of bounds low")
72 __failure __msg("invalid write to stack R1 off=-79992 size=8")
73 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
74 __naked void load_out_of_bounds_low(void)
75 {
76 	asm volatile ("					\
77 	r1 = r10;					\
78 	r1 += -80000;					\
79 	r0 = 0xfaceb00c;				\
80 	*(u64*)(r1 + 8) = r0;				\
81 	r0 = *(u64*)(r1 + 8);				\
82 	exit;						\
83 "	::: __clobber_all);
84 }
85 
86 SEC("socket")
87 __description("PTR_TO_STACK store/load - out of bounds high")
88 __failure __msg("invalid write to stack R1 off=0 size=8")
89 __failure_unpriv
90 __naked void load_out_of_bounds_high(void)
91 {
92 	asm volatile ("					\
93 	r1 = r10;					\
94 	r1 += -8;					\
95 	r0 = 0xfaceb00c;				\
96 	*(u64*)(r1 + 8) = r0;				\
97 	r0 = *(u64*)(r1 + 8);				\
98 	exit;						\
99 "	::: __clobber_all);
100 }
101 
102 SEC("socket")
103 __description("PTR_TO_STACK check high 1")
104 __success __success_unpriv __retval(42)
105 __naked void to_stack_check_high_1(void)
106 {
107 	asm volatile ("					\
108 	r1 = r10;					\
109 	r1 += -1;					\
110 	r0 = 42;					\
111 	*(u8*)(r1 + 0) = r0;				\
112 	r0 = *(u8*)(r1 + 0);				\
113 	exit;						\
114 "	::: __clobber_all);
115 }
116 
117 SEC("socket")
118 __description("PTR_TO_STACK check high 2")
119 __success __success_unpriv __retval(42)
120 __naked void to_stack_check_high_2(void)
121 {
122 	asm volatile ("					\
123 	r1 = r10;					\
124 	r0 = 42;					\
125 	*(u8*)(r1 - 1) = r0;				\
126 	r0 = *(u8*)(r1 - 1);				\
127 	exit;						\
128 "	::: __clobber_all);
129 }
130 
131 SEC("socket")
132 __description("PTR_TO_STACK check high 3")
133 __success __failure_unpriv
134 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
135 __retval(42)
136 __naked void to_stack_check_high_3(void)
137 {
138 	asm volatile ("					\
139 	r1 = r10;					\
140 	r1 += 0;					\
141 	r0 = 42;					\
142 	*(u8*)(r1 - 1) = r0;				\
143 	r0 = *(u8*)(r1 - 1);				\
144 	exit;						\
145 "	::: __clobber_all);
146 }
147 
148 SEC("socket")
149 __description("PTR_TO_STACK check high 4")
150 __failure __msg("invalid write to stack R1 off=0 size=1")
151 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
152 __naked void to_stack_check_high_4(void)
153 {
154 	asm volatile ("					\
155 	r1 = r10;					\
156 	r1 += 0;					\
157 	r0 = 42;					\
158 	*(u8*)(r1 + 0) = r0;				\
159 	r0 = *(u8*)(r1 + 0);				\
160 	exit;						\
161 "	::: __clobber_all);
162 }
163 
164 SEC("socket")
165 __description("PTR_TO_STACK check high 5")
166 __failure __msg("invalid write to stack R1")
167 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
168 __naked void to_stack_check_high_5(void)
169 {
170 	asm volatile ("					\
171 	r1 = r10;					\
172 	r1 += %[__imm_0];				\
173 	r0 = 42;					\
174 	*(u8*)(r1 + 0) = r0;				\
175 	r0 = *(u8*)(r1 + 0);				\
176 	exit;						\
177 "	:
178 	: __imm_const(__imm_0, (1 << 29) - 1)
179 	: __clobber_all);
180 }
181 
182 SEC("socket")
183 __description("PTR_TO_STACK check high 6")
184 __failure __msg("invalid write to stack")
185 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
186 __naked void to_stack_check_high_6(void)
187 {
188 	asm volatile ("					\
189 	r1 = r10;					\
190 	r1 += %[__imm_0];				\
191 	r0 = 42;					\
192 	*(u8*)(r1 + %[shrt_max]) = r0;			\
193 	r0 = *(u8*)(r1 + %[shrt_max]);			\
194 	exit;						\
195 "	:
196 	: __imm_const(__imm_0, (1 << 29) - 1),
197 	  __imm_const(shrt_max, SHRT_MAX)
198 	: __clobber_all);
199 }
200 
201 SEC("socket")
202 __description("PTR_TO_STACK check high 7")
203 __failure __msg("fp pointer offset")
204 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
205 __naked void to_stack_check_high_7(void)
206 {
207 	asm volatile ("					\
208 	r1 = r10;					\
209 	r1 += %[__imm_0];				\
210 	r1 += %[__imm_0];				\
211 	r0 = 42;					\
212 	*(u8*)(r1 + %[shrt_max]) = r0;			\
213 	r0 = *(u8*)(r1 + %[shrt_max]);			\
214 	exit;						\
215 "	:
216 	: __imm_const(__imm_0, (1 << 29) - 1),
217 	  __imm_const(shrt_max, SHRT_MAX)
218 	: __clobber_all);
219 }
220 
221 SEC("socket")
222 __description("PTR_TO_STACK check low 1")
223 __success __success_unpriv __retval(42)
224 __naked void to_stack_check_low_1(void)
225 {
226 	asm volatile ("					\
227 	r1 = r10;					\
228 	r1 += -512;					\
229 	r0 = 42;					\
230 	*(u8*)(r1 + 0) = r0;				\
231 	r0 = *(u8*)(r1 + 0);				\
232 	exit;						\
233 "	::: __clobber_all);
234 }
235 
236 SEC("socket")
237 __description("PTR_TO_STACK check low 2")
238 __success __failure_unpriv
239 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
240 __retval(42)
241 __naked void to_stack_check_low_2(void)
242 {
243 	asm volatile ("					\
244 	r1 = r10;					\
245 	r1 += -513;					\
246 	r0 = 42;					\
247 	*(u8*)(r1 + 1) = r0;				\
248 	r0 = *(u8*)(r1 + 1);				\
249 	exit;						\
250 "	::: __clobber_all);
251 }
252 
253 SEC("socket")
254 __description("PTR_TO_STACK check low 3")
255 __failure __msg("invalid write to stack R1 off=-513 size=1")
256 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
257 __naked void to_stack_check_low_3(void)
258 {
259 	asm volatile ("					\
260 	r1 = r10;					\
261 	r1 += -513;					\
262 	r0 = 42;					\
263 	*(u8*)(r1 + 0) = r0;				\
264 	r0 = *(u8*)(r1 + 0);				\
265 	exit;						\
266 "	::: __clobber_all);
267 }
268 
269 SEC("socket")
270 __description("PTR_TO_STACK check low 4")
271 __failure __msg("math between fp pointer")
272 __failure_unpriv
273 __naked void to_stack_check_low_4(void)
274 {
275 	asm volatile ("					\
276 	r1 = r10;					\
277 	r1 += %[int_min];				\
278 	r0 = 42;					\
279 	*(u8*)(r1 + 0) = r0;				\
280 	r0 = *(u8*)(r1 + 0);				\
281 	exit;						\
282 "	:
283 	: __imm_const(int_min, INT_MIN)
284 	: __clobber_all);
285 }
286 
287 SEC("socket")
288 __description("PTR_TO_STACK check low 5")
289 __failure __msg("invalid write to stack")
290 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
291 __naked void to_stack_check_low_5(void)
292 {
293 	asm volatile ("					\
294 	r1 = r10;					\
295 	r1 += %[__imm_0];				\
296 	r0 = 42;					\
297 	*(u8*)(r1 + 0) = r0;				\
298 	r0 = *(u8*)(r1 + 0);				\
299 	exit;						\
300 "	:
301 	: __imm_const(__imm_0, -((1 << 29) - 1))
302 	: __clobber_all);
303 }
304 
305 SEC("socket")
306 __description("PTR_TO_STACK check low 6")
307 __failure __msg("invalid write to stack")
308 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
309 __naked void to_stack_check_low_6(void)
310 {
311 	asm volatile ("					\
312 	r1 = r10;					\
313 	r1 += %[__imm_0];				\
314 	r0 = 42;					\
315 	*(u8*)(r1  %[shrt_min]) = r0;			\
316 	r0 = *(u8*)(r1  %[shrt_min]);			\
317 	exit;						\
318 "	:
319 	: __imm_const(__imm_0, -((1 << 29) - 1)),
320 	  __imm_const(shrt_min, SHRT_MIN)
321 	: __clobber_all);
322 }
323 
324 SEC("socket")
325 __description("PTR_TO_STACK check low 7")
326 __failure __msg("fp pointer offset")
327 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
328 __naked void to_stack_check_low_7(void)
329 {
330 	asm volatile ("					\
331 	r1 = r10;					\
332 	r1 += %[__imm_0];				\
333 	r1 += %[__imm_0];				\
334 	r0 = 42;					\
335 	*(u8*)(r1  %[shrt_min]) = r0;			\
336 	r0 = *(u8*)(r1  %[shrt_min]);			\
337 	exit;						\
338 "	:
339 	: __imm_const(__imm_0, -((1 << 29) - 1)),
340 	  __imm_const(shrt_min, SHRT_MIN)
341 	: __clobber_all);
342 }
343 
344 SEC("socket")
345 __description("PTR_TO_STACK mixed reg/k, 1")
346 __success __success_unpriv __retval(42)
347 __naked void stack_mixed_reg_k_1(void)
348 {
349 	asm volatile ("					\
350 	r1 = r10;					\
351 	r1 += -3;					\
352 	r2 = -3;					\
353 	r1 += r2;					\
354 	r0 = 42;					\
355 	*(u8*)(r1 + 0) = r0;				\
356 	r0 = *(u8*)(r1 + 0);				\
357 	exit;						\
358 "	::: __clobber_all);
359 }
360 
361 SEC("socket")
362 __description("PTR_TO_STACK mixed reg/k, 2")
363 __success __success_unpriv __retval(42)
364 __naked void stack_mixed_reg_k_2(void)
365 {
366 	asm volatile ("					\
367 	r0 = 0;						\
368 	*(u64*)(r10 - 8) = r0;				\
369 	r0 = 0;						\
370 	*(u64*)(r10 - 16) = r0;				\
371 	r1 = r10;					\
372 	r1 += -3;					\
373 	r2 = -3;					\
374 	r1 += r2;					\
375 	r0 = 42;					\
376 	*(u8*)(r1 + 0) = r0;				\
377 	r5 = r10;					\
378 	r0 = *(u8*)(r5 - 6);				\
379 	exit;						\
380 "	::: __clobber_all);
381 }
382 
383 SEC("socket")
384 __description("PTR_TO_STACK mixed reg/k, 3")
385 __success __success_unpriv __retval(-3)
386 __naked void stack_mixed_reg_k_3(void)
387 {
388 	asm volatile ("					\
389 	r1 = r10;					\
390 	r1 += -3;					\
391 	r2 = -3;					\
392 	r1 += r2;					\
393 	r0 = 42;					\
394 	*(u8*)(r1 + 0) = r0;				\
395 	r0 = r2;					\
396 	exit;						\
397 "	::: __clobber_all);
398 }
399 
400 SEC("socket")
401 __description("PTR_TO_STACK reg")
402 __success __success_unpriv __retval(42)
403 __naked void ptr_to_stack_reg(void)
404 {
405 	asm volatile ("					\
406 	r1 = r10;					\
407 	r2 = -3;					\
408 	r1 += r2;					\
409 	r0 = 42;					\
410 	*(u8*)(r1 + 0) = r0;				\
411 	r0 = *(u8*)(r1 + 0);				\
412 	exit;						\
413 "	::: __clobber_all);
414 }
415 
416 SEC("socket")
417 __description("stack pointer arithmetic")
418 __success __success_unpriv __retval(0)
419 __naked void stack_pointer_arithmetic(void)
420 {
421 	asm volatile ("					\
422 	r1 = 4;						\
423 	goto l0_%=;					\
424 l0_%=:	r7 = r10;					\
425 	r7 += -10;					\
426 	r7 += -10;					\
427 	r2 = r7;					\
428 	r2 += r1;					\
429 	r0 = 0;						\
430 	*(u32*)(r2 + 4) = r0;				\
431 	r2 = r7;					\
432 	r2 += 8;					\
433 	r0 = 0;						\
434 	*(u32*)(r2 + 4) = r0;				\
435 	r0 = 0;						\
436 	exit;						\
437 "	::: __clobber_all);
438 }
439 
440 SEC("tc")
441 __description("store PTR_TO_STACK in R10 to array map using BPF_B")
442 __success __retval(42)
443 __naked void array_map_using_bpf_b(void)
444 {
445 	asm volatile ("					\
446 	/* Load pointer to map. */			\
447 	r2 = r10;					\
448 	r2 += -8;					\
449 	r1 = 0;						\
450 	*(u64*)(r2 + 0) = r1;				\
451 	r1 = %[map_array_48b] ll;			\
452 	call %[bpf_map_lookup_elem];			\
453 	if r0 != 0 goto l0_%=;				\
454 	r0 = 2;						\
455 	exit;						\
456 l0_%=:	r1 = r0;					\
457 	/* Copy R10 to R9. */				\
458 	r9 = r10;					\
459 	/* Pollute other registers with unaligned values. */\
460 	r2 = -1;					\
461 	r3 = -1;					\
462 	r4 = -1;					\
463 	r5 = -1;					\
464 	r6 = -1;					\
465 	r7 = -1;					\
466 	r8 = -1;					\
467 	/* Store both R9 and R10 with BPF_B and read back. */\
468 	*(u8*)(r1 + 0) = r10;				\
469 	r2 = *(u8*)(r1 + 0);				\
470 	*(u8*)(r1 + 0) = r9;				\
471 	r3 = *(u8*)(r1 + 0);				\
472 	/* Should read back as same value. */		\
473 	if r2 == r3 goto l1_%=;				\
474 	r0 = 1;						\
475 	exit;						\
476 l1_%=:	r0 = 42;					\
477 	exit;						\
478 "	:
479 	: __imm(bpf_map_lookup_elem),
480 	  __imm_addr(map_array_48b)
481 	: __clobber_all);
482 }
483 
484 char _license[] SEC("license") = "GPL";
485