xref: /linux/tools/testing/selftests/bpf/progs/verifier_stack_arg_order.c (revision b2128290c29902315e632ea59e0504d6bc9e9b42)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Copyright (c) 2026 Meta Platforms, Inc. and affiliates. */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7 
8 #if (defined(__TARGET_ARCH_x86) || defined(__TARGET_ARCH_arm64)) && \
9 	defined(__BPF_FEATURE_STACK_ARGUMENT)
10 
11 __noinline __used __naked
12 static int subprog_bad_order_6args(int a, int b, int c, int d, int e, int f)
13 {
14 	asm volatile (
15 		"*(u64 *)(r11 - 8) = r1;"
16 		"r0 = *(u64 *)(r11 + 8);"
17 		"exit;"
18 		::: __clobber_all
19 	);
20 }
21 
22 SEC("tc")
23 __description("stack_arg: r11 load after r11 store")
24 __failure
25 __msg("r11 load must be before any r11 store or call insn")
26 __btf_func_path("btf__verifier_stack_arg_order.bpf.o")
27 __naked void stack_arg_load_after_store(void)
28 {
29 	asm volatile (
30 		"r1 = 1;"
31 		"r2 = 2;"
32 		"r3 = 3;"
33 		"r4 = 4;"
34 		"r5 = 5;"
35 		"*(u64 *)(r11 - 8) = 6;"
36 		"call subprog_bad_order_6args;"
37 		"exit;"
38 		::: __clobber_all
39 	);
40 }
41 
42 __noinline __used __naked
43 static int subprog_call_before_load_6args(int a, int b, int c, int d, int e,
44 					  int f)
45 {
46 	asm volatile (
47 		"call %[bpf_get_prandom_u32];"
48 		"r0 = *(u64 *)(r11 + 8);"
49 		"exit;"
50 		:: __imm(bpf_get_prandom_u32)
51 		: __clobber_all
52 	);
53 }
54 
55 SEC("tc")
56 __description("stack_arg: r11 load after a call")
57 __failure
58 __msg("r11 load must be before any r11 store or call insn")
59 __btf_func_path("btf__verifier_stack_arg_order.bpf.o")
60 __naked void stack_arg_load_after_call(void)
61 {
62 	asm volatile (
63 		"r1 = 1;"
64 		"r2 = 2;"
65 		"r3 = 3;"
66 		"r4 = 4;"
67 		"r5 = 5;"
68 		"*(u64 *)(r11 - 8) = 6;"
69 		"call subprog_call_before_load_6args;"
70 		"exit;"
71 		::: __clobber_all
72 	);
73 }
74 
75 __noinline __used __naked
76 static int subprog_pruning_call_before_load_6args(int a, int b, int c, int d,
77 						  int e, int f)
78 {
79 	asm volatile (
80 		"if r1 s> 0 goto l0_%=;"
81 		"goto l1_%=;"
82 	"l0_%=:"
83 		"call %[bpf_get_prandom_u32];"
84 	"l1_%=:"
85 		"r0 = *(u64 *)(r11 + 8);"
86 		"exit;"
87 		:: __imm(bpf_get_prandom_u32)
88 		: __clobber_all
89 	);
90 }
91 
92 SEC("tc")
93 __description("stack_arg: pruning keeps r11 load ordering")
94 __failure
95 __flag(BPF_F_TEST_STATE_FREQ)
96 __msg("r11 load must be before any r11 store or call insn")
97 __btf_func_path("btf__verifier_stack_arg_order.bpf.o")
98 __naked void stack_arg_pruning_load_after_call(void)
99 {
100 	asm volatile (
101 		"call %[bpf_get_prandom_u32];"
102 		"r1 = r0;"
103 		"r2 = 2;"
104 		"r3 = 3;"
105 		"r4 = 4;"
106 		"r5 = 5;"
107 		"*(u64 *)(r11 - 8) = 6;"
108 		"call subprog_pruning_call_before_load_6args;"
109 		"exit;"
110 		:: __imm(bpf_get_prandom_u32)
111 		: __clobber_all
112 	);
113 }
114 
115 /*
116  * "bad_ptr": the first arg is 'long *', which is not a recognized pointer
117  * type for static subprogs (not ctx, dynptr, or tagged).  btf_prepare_func_args()
118  * sets arg_cnt = 7 / stack_arg_cnt = 2, then fails with -EINVAL.  The subprog
119  * is marked unreliable but the call still proceeds for static subprogs.
120  */
121 __noinline __used __naked
122 static void subprog_bad_ptr_7args(long *a, int b, int c, int d, int e, int f, int g)
123 {
124 	asm volatile (
125 		"r0 = *(u64 *)(r11 + 8);"
126 		"r1 = *(u64 *)(r11 + 16);"
127 		"exit;"
128 		::: __clobber_all
129 	);
130 }
131 
132 SEC("tc")
133 __description("stack_arg: read without caller write")
134 __failure
135 __msg("callee expects 7 args, stack arg1 is not initialized")
136 __btf_func_path("btf__verifier_stack_arg_order.bpf.o")
137 __naked void stack_arg_read_without_write_1(void)
138 {
139 	asm volatile (
140 		"r1 = 0;"
141 		"r2 = 0;"
142 		"r3 = 0;"
143 		"r4 = 0;"
144 		"r5 = 0;"
145 		"call subprog_bad_ptr_7args;"
146 		"exit;"
147 		::: __clobber_all
148 	);
149 }
150 
151 SEC("tc")
152 __description("stack_arg: read with not-initialized caller write")
153 __failure
154 __msg("R0 !read_ok")
155 __btf_func_path("btf__verifier_stack_arg_order.bpf.o")
156 __naked void stack_arg_read_without_write_2(void)
157 {
158 	asm volatile (
159 		"r1 = 0;"
160 		"r2 = 0;"
161 		"r3 = 0;"
162 		"r4 = 0;"
163 		"r5 = 0;"
164 		"*(u64 *)(r11 - 8) = 0;"
165 		"*(u64 *)(r11 - 16) = 0;"
166 		"call subprog_bad_ptr_7args;"
167 		"call subprog_bad_ptr_7args;"
168 		"exit;"
169 		::: __clobber_all
170 	);
171 }
172 
173 #else
174 
175 SEC("socket")
176 __description("stack_arg order is not supported by compiler or jit, use a dummy test")
177 __success
178 int dummy_test(void)
179 {
180 	return 0;
181 }
182 
183 #endif
184 
185 char _license[] SEC("license") = "GPL";
186