xref: /linux/tools/testing/selftests/bpf/progs/verifier_stack_ptr.c (revision 1cec0b70fdc678d6c0252f3f25e23df0b5a5e49c)
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 -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 -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 __load_if_no_large_stack()
239 __success __failure_unpriv
240 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
241 __retval(42)
242 __naked void to_stack_check_low_2(void)
243 {
244 	asm volatile ("					\
245 	r1 = r10;					\
246 	r1 += -513;					\
247 	r0 = 42;					\
248 	*(u8*)(r1 + 1) = r0;				\
249 	r0 = *(u8*)(r1 + 1);				\
250 	exit;						\
251 "	::: __clobber_all);
252 }
253 
254 SEC("socket")
255 __description("PTR_TO_STACK check low 2, large stack")
256 __load_if_large_stack()
257 __success __failure_unpriv
258 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
259 __retval(42)
260 __naked void to_stack_check_low_2_large(void)
261 {
262 	asm volatile ("					\
263 	r1 = r10;					\
264 	r1 += -2049;					\
265 	r0 = 42;					\
266 	*(u8*)(r1 + 1) = r0;				\
267 	r0 = *(u8*)(r1 + 1);				\
268 	exit;						\
269 "	::: __clobber_all);
270 }
271 
272 SEC("socket")
273 __description("PTR_TO_STACK check low 3")
274 __load_if_no_large_stack()
275 __failure __msg("invalid write to stack R1 off=-513 size=1")
276 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
277 __naked void to_stack_check_low_3(void)
278 {
279 	asm volatile ("					\
280 	r1 = r10;					\
281 	r1 += -513;					\
282 	r0 = 42;					\
283 	*(u8*)(r1 + 0) = r0;				\
284 	r0 = *(u8*)(r1 + 0);				\
285 	exit;						\
286 "	::: __clobber_all);
287 }
288 
289 SEC("socket")
290 __description("PTR_TO_STACK check low 3, large stack")
291 __load_if_large_stack()
292 __failure __msg("invalid write to stack R1 off=-2049 size=1")
293 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
294 __naked void to_stack_check_low_3_large(void)
295 {
296 	asm volatile ("					\
297 	r1 = r10;					\
298 	r1 += -2049;					\
299 	r0 = 42;					\
300 	*(u8*)(r1 + 0) = r0;				\
301 	r0 = *(u8*)(r1 + 0);				\
302 	exit;						\
303 "	::: __clobber_all);
304 }
305 
306 SEC("socket")
307 __description("PTR_TO_STACK check low 4")
308 __failure __msg("math between fp pointer")
309 __failure_unpriv
310 __naked void to_stack_check_low_4(void)
311 {
312 	asm volatile ("					\
313 	r1 = r10;					\
314 	r1 += %[int_min];				\
315 	r0 = 42;					\
316 	*(u8*)(r1 + 0) = r0;				\
317 	r0 = *(u8*)(r1 + 0);				\
318 	exit;						\
319 "	:
320 	: __imm_const(int_min, INT_MIN)
321 	: __clobber_all);
322 }
323 
324 SEC("socket")
325 __description("PTR_TO_STACK check low 5")
326 __failure __msg("invalid write to stack")
327 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
328 __naked void to_stack_check_low_5(void)
329 {
330 	asm volatile ("					\
331 	r1 = r10;					\
332 	r1 += %[__imm_0];				\
333 	r0 = 42;					\
334 	*(u8*)(r1 + 0) = r0;				\
335 	r0 = *(u8*)(r1 + 0);				\
336 	exit;						\
337 "	:
338 	: __imm_const(__imm_0, -((1 << 29) - 1))
339 	: __clobber_all);
340 }
341 
342 SEC("socket")
343 __description("PTR_TO_STACK check low 6")
344 __failure __msg("invalid write to stack")
345 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
346 __naked void to_stack_check_low_6(void)
347 {
348 	asm volatile ("					\
349 	r1 = r10;					\
350 	r1 += %[__imm_0];				\
351 	r0 = 42;					\
352 	*(u8*)(r1  %[shrt_min]) = r0;			\
353 	r0 = *(u8*)(r1  %[shrt_min]);			\
354 	exit;						\
355 "	:
356 	: __imm_const(__imm_0, -((1 << 29) - 1)),
357 	  __imm_const(shrt_min, SHRT_MIN)
358 	: __clobber_all);
359 }
360 
361 SEC("socket")
362 __description("PTR_TO_STACK check low 7")
363 __failure __msg("fp pointer offset")
364 __msg_unpriv("R1 stack pointer arithmetic goes out of range")
365 __naked void to_stack_check_low_7(void)
366 {
367 	asm volatile ("					\
368 	r1 = r10;					\
369 	r1 += %[__imm_0];				\
370 	r1 += %[__imm_0];				\
371 	r0 = 42;					\
372 	*(u8*)(r1  %[shrt_min]) = r0;			\
373 	r0 = *(u8*)(r1  %[shrt_min]);			\
374 	exit;						\
375 "	:
376 	: __imm_const(__imm_0, -((1 << 29) - 1)),
377 	  __imm_const(shrt_min, SHRT_MIN)
378 	: __clobber_all);
379 }
380 
381 SEC("socket")
382 __description("PTR_TO_STACK mixed reg/k, 1")
383 __success __success_unpriv __retval(42)
384 __naked void stack_mixed_reg_k_1(void)
385 {
386 	asm volatile ("					\
387 	r1 = r10;					\
388 	r1 += -3;					\
389 	r2 = -3;					\
390 	r1 += r2;					\
391 	r0 = 42;					\
392 	*(u8*)(r1 + 0) = r0;				\
393 	r0 = *(u8*)(r1 + 0);				\
394 	exit;						\
395 "	::: __clobber_all);
396 }
397 
398 SEC("socket")
399 __description("PTR_TO_STACK mixed reg/k, 2")
400 __success __success_unpriv __retval(42)
401 __naked void stack_mixed_reg_k_2(void)
402 {
403 	asm volatile ("					\
404 	r0 = 0;						\
405 	*(u64*)(r10 - 8) = r0;				\
406 	r0 = 0;						\
407 	*(u64*)(r10 - 16) = r0;				\
408 	r1 = r10;					\
409 	r1 += -3;					\
410 	r2 = -3;					\
411 	r1 += r2;					\
412 	r0 = 42;					\
413 	*(u8*)(r1 + 0) = r0;				\
414 	r5 = r10;					\
415 	r0 = *(u8*)(r5 - 6);				\
416 	exit;						\
417 "	::: __clobber_all);
418 }
419 
420 SEC("socket")
421 __description("PTR_TO_STACK mixed reg/k, 3")
422 __success __success_unpriv __retval(-3)
423 __naked void stack_mixed_reg_k_3(void)
424 {
425 	asm volatile ("					\
426 	r1 = r10;					\
427 	r1 += -3;					\
428 	r2 = -3;					\
429 	r1 += r2;					\
430 	r0 = 42;					\
431 	*(u8*)(r1 + 0) = r0;				\
432 	r0 = r2;					\
433 	exit;						\
434 "	::: __clobber_all);
435 }
436 
437 SEC("socket")
438 __description("PTR_TO_STACK reg")
439 __success __success_unpriv __retval(42)
440 __naked void ptr_to_stack_reg(void)
441 {
442 	asm volatile ("					\
443 	r1 = r10;					\
444 	r2 = -3;					\
445 	r1 += r2;					\
446 	r0 = 42;					\
447 	*(u8*)(r1 + 0) = r0;				\
448 	r0 = *(u8*)(r1 + 0);				\
449 	exit;						\
450 "	::: __clobber_all);
451 }
452 
453 SEC("socket")
454 __description("stack pointer arithmetic")
455 __success __success_unpriv __retval(0)
456 __naked void stack_pointer_arithmetic(void)
457 {
458 	asm volatile ("					\
459 	r1 = 4;						\
460 	goto l0_%=;					\
461 l0_%=:	r7 = r10;					\
462 	r7 += -10;					\
463 	r7 += -10;					\
464 	r2 = r7;					\
465 	r2 += r1;					\
466 	r0 = 0;						\
467 	*(u32*)(r2 + 4) = r0;				\
468 	r2 = r7;					\
469 	r2 += 8;					\
470 	r0 = 0;						\
471 	*(u32*)(r2 + 4) = r0;				\
472 	r0 = 0;						\
473 	exit;						\
474 "	::: __clobber_all);
475 }
476 
477 SEC("tc")
478 __description("store PTR_TO_STACK in R10 to array map using BPF_B")
479 __success __retval(42)
480 __naked void array_map_using_bpf_b(void)
481 {
482 	asm volatile ("					\
483 	/* Load pointer to map. */			\
484 	r2 = r10;					\
485 	r2 += -8;					\
486 	r1 = 0;						\
487 	*(u64*)(r2 + 0) = r1;				\
488 	r1 = %[map_array_48b] ll;			\
489 	call %[bpf_map_lookup_elem];			\
490 	if r0 != 0 goto l0_%=;				\
491 	r0 = 2;						\
492 	exit;						\
493 l0_%=:	r1 = r0;					\
494 	/* Copy R10 to R9. */				\
495 	r9 = r10;					\
496 	/* Pollute other registers with unaligned values. */\
497 	r2 = -1;					\
498 	r3 = -1;					\
499 	r4 = -1;					\
500 	r5 = -1;					\
501 	r6 = -1;					\
502 	r7 = -1;					\
503 	r8 = -1;					\
504 	/* Store both R9 and R10 with BPF_B and read back. */\
505 	*(u8*)(r1 + 0) = r10;				\
506 	r2 = *(u8*)(r1 + 0);				\
507 	*(u8*)(r1 + 0) = r9;				\
508 	r3 = *(u8*)(r1 + 0);				\
509 	/* Should read back as same value. */		\
510 	if r2 == r3 goto l1_%=;				\
511 	r0 = 1;						\
512 	exit;						\
513 l1_%=:	r0 = 42;					\
514 	exit;						\
515 "	:
516 	: __imm(bpf_map_lookup_elem),
517 	  __imm_addr(map_array_48b)
518 	: __clobber_all);
519 }
520 
521 SEC("socket")
522 __description("PTR_TO_STACK stack size > 512")
523 __load_if_no_large_stack()
524 __failure __msg("invalid write to stack R1 off=-520 size=8")
525 __naked void stack_check_size_gt_512(void)
526 {
527 	asm volatile ("					\
528 	r1 = r10;					\
529 	r1 += -520;					\
530 	r0 = 42;					\
531 	*(u64*)(r1 + 0) = r0;				\
532 	exit;						\
533 "	::: __clobber_all);
534 }
535 
536 SEC("socket")
537 __description("PTR_TO_STACK stack size > 2048")
538 __load_if_large_stack()
539 __failure __msg("invalid write to stack R1 off=-2056 size=8")
540 __naked void stack_check_size_gt_2048(void)
541 {
542 	asm volatile ("					\
543 	r1 = r10;					\
544 	r1 += -2056;					\
545 	r0 = 42;					\
546 	*(u64*)(r1 + 0) = r0;				\
547 	exit;						\
548 "	::: __clobber_all);
549 }
550 
551 #ifdef __BPF_FEATURE_MAY_GOTO
552 SEC("socket")
553 __description("PTR_TO_STACK stack size 512 with may_goto with jit")
554 __load_if_JITed()
555 __success __retval(42)
556 __naked void stack_check_size_512_with_may_goto_jit(void)
557 {
558 	asm volatile ("					\
559 	r1 = r10;					\
560 	r1 += -512;					\
561 	r0 = 42;					\
562 	*(u32*)(r1 + 0) = r0;				\
563 	may_goto l0_%=;					\
564 	r2 = 100;					\
565 	l0_%=:						\
566 	exit;						\
567 "	::: __clobber_all);
568 }
569 
570 SEC("socket")
571 __description("PTR_TO_STACK stack size 512 with may_goto without jit")
572 __load_if_no_JITed()
573 __failure __msg("stack size 520(extra 8) is too large")
574 __naked void stack_check_size_512_with_may_goto(void)
575 {
576 	asm volatile ("					\
577 	r1 = r10;					\
578 	r1 += -512;					\
579 	r0 = 42;					\
580 	*(u32*)(r1 + 0) = r0;				\
581 	may_goto l0_%=;					\
582 	r2 = 100;					\
583 	l0_%=:						\
584 	exit;						\
585 "	::: __clobber_all);
586 }
587 #endif
588 
589 /*
590  * Verify that old PTR_TO_STACK state is considered a super-set of
591  * new PTR_TO_STACK state when new variable range is a sub-range
592  * of the old range, e.g. old [-72, -16] vs new [-64, -16].
593  */
594 SEC("socket")
595 __success
596 __log_level(2)
597 __flag(BPF_F_TEST_STATE_FREQ)
598 /*
599  * r7 is widened to [-72, -16] at the loop header (insn 3),
600  * the loop body sees [-64, -8] after 'r7 += 8'
601  */
602 __msg("loop header at 3, widening r7 to -72..-16 step 8")
603 __msg("R7=fp(smin=smin32=-64,smax=smax32=-8")
604 /*
605  * back-edge state is clamped to the remaining iterations and pruned
606  * at the header, because [-64, -16] is within the widened [-72, -16]
607  */
608 __msg("loop header at 3, clamping r7 to -64..-16 step 8")
609 __msg("from 5 to 3: safe")
610 __msg("processed 9 insns")
611 __naked void stack_ptr_subrange_in_loop(void)
612 {
613 	asm volatile ("					\
614 	r7 = r10;					\
615 	r7 += -72;					\
616 	r6 = 0;						\
617 	r7 += 8;					\
618 	r6 += 1;					\
619 	if r6 < 8 goto -3;				\
620 	r0 = 0;						\
621 	exit;						\
622 "	::: __clobber_all);
623 }
624 
625 char _license[] SEC("license") = "GPL";
626