xref: /linux/tools/testing/selftests/bpf/progs/verifier_ld_ind.c (revision b9f9044be76f8494ff48bbf438ecc5b15e7bd3d4)
1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/ld_ind.c */
3 
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "../../../include/linux/filter.h"
7 #include "bpf_misc.h"
8 
9 SEC("socket")
10 __description("ld_ind: check calling conv, r1")
11 __failure __msg("R1 !read_ok")
12 __failure_unpriv
13 __naked void ind_check_calling_conv_r1(void)
14 {
15 	asm volatile ("					\
16 	r6 = r1;					\
17 	r1 = 1;						\
18 	.8byte %[ld_ind];				\
19 	r0 = r1;					\
20 	exit;						\
21 "	:
22 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_1, -0x200000))
23 	: __clobber_all);
24 }
25 
26 SEC("socket")
27 __description("ld_ind: check calling conv, r2")
28 __failure __msg("R2 !read_ok")
29 __failure_unpriv
30 __naked void ind_check_calling_conv_r2(void)
31 {
32 	asm volatile ("					\
33 	r6 = r1;					\
34 	r2 = 1;						\
35 	.8byte %[ld_ind];				\
36 	r0 = r2;					\
37 	exit;						\
38 "	:
39 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_2, -0x200000))
40 	: __clobber_all);
41 }
42 
43 SEC("socket")
44 __description("ld_ind: check calling conv, r3")
45 __failure __msg("R3 !read_ok")
46 __failure_unpriv
47 __naked void ind_check_calling_conv_r3(void)
48 {
49 	asm volatile ("					\
50 	r6 = r1;					\
51 	r3 = 1;						\
52 	.8byte %[ld_ind];				\
53 	r0 = r3;					\
54 	exit;						\
55 "	:
56 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_3, -0x200000))
57 	: __clobber_all);
58 }
59 
60 SEC("socket")
61 __description("ld_ind: check calling conv, r4")
62 __failure __msg("R4 !read_ok")
63 __failure_unpriv
64 __naked void ind_check_calling_conv_r4(void)
65 {
66 	asm volatile ("					\
67 	r6 = r1;					\
68 	r4 = 1;						\
69 	.8byte %[ld_ind];				\
70 	r0 = r4;					\
71 	exit;						\
72 "	:
73 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_4, -0x200000))
74 	: __clobber_all);
75 }
76 
77 SEC("socket")
78 __description("ld_ind: check calling conv, r5")
79 __failure __msg("R5 !read_ok")
80 __failure_unpriv
81 __naked void ind_check_calling_conv_r5(void)
82 {
83 	asm volatile ("					\
84 	r6 = r1;					\
85 	r5 = 1;						\
86 	.8byte %[ld_ind];				\
87 	r0 = r5;					\
88 	exit;						\
89 "	:
90 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_5, -0x200000))
91 	: __clobber_all);
92 }
93 
94 SEC("socket")
95 __description("ld_ind: check calling conv, r7")
96 __success __success_unpriv __retval(1)
97 __naked void ind_check_calling_conv_r7(void)
98 {
99 	asm volatile ("					\
100 	r6 = r1;					\
101 	r7 = 1;						\
102 	.8byte %[ld_ind];				\
103 	r0 = r7;					\
104 	exit;						\
105 "	:
106 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, -0x200000))
107 	: __clobber_all);
108 }
109 
110 /*
111  * ld_{abs,ind} subprog that always sets r0=1 on the success path.
112  * bpf_gen_ld_abs() emits a hidden exit with r0=0 when the load helper
113  * fails. The verifier must model this failure return so that callers
114  * account for r0=0 as a possible return value.
115  */
116 __naked __noinline __used
117 static int ldabs_subprog(void)
118 {
119 	asm volatile (
120 	"r6 = r1;"
121 	".8byte %[ld_abs];"
122 	"r0 = 1;"
123 	"exit;"
124 	:
125 	: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
126 	: __clobber_all);
127 }
128 
129 __naked __noinline __used
130 static int ldind_subprog(void)
131 {
132 	asm volatile (
133 	"r6 = r1;"
134 	"r7 = 0;"
135 	".8byte %[ld_ind];"
136 	"r0 = 1;"
137 	"exit;"
138 	:
139 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
140 	: __clobber_all);
141 }
142 
143 SEC("socket")
144 __description("ld_abs: subprog early exit on ld_abs failure")
145 __failure __msg("R9 !read_ok")
146 __naked void ld_abs_subprog_early_exit(void)
147 {
148 	asm volatile (
149 	"call ldabs_subprog;"
150 	"if r0 != 0 goto l_exit_%=;"
151 	"r0 = r9;"
152 	"l_exit_%=:"
153 	"r0 = 0;"
154 	"exit;"
155 	::: __clobber_all);
156 }
157 
158 SEC("socket")
159 __description("ld_ind: subprog early exit on ld_ind failure")
160 __failure __msg("R9 !read_ok")
161 __naked void ld_ind_subprog_early_exit(void)
162 {
163 	asm volatile (
164 	"call ldind_subprog;"
165 	"if r0 != 0 goto l_exit_%=;"
166 	"r0 = r9;"
167 	"l_exit_%=:"
168 	"r0 = 0;"
169 	"exit;"
170 	::: __clobber_all);
171 }
172 
173 SEC("socket")
174 __description("ld_abs: subprog with both paths safe")
175 __success
176 __naked void ld_abs_subprog_both_paths_safe(void)
177 {
178 	asm volatile (
179 	"call ldabs_subprog;"
180 	"r0 = 0;"
181 	"exit;"
182 	::: __clobber_all);
183 }
184 
185 SEC("socket")
186 __description("ld_ind: subprog with both paths safe")
187 __success
188 __naked void ld_ind_subprog_both_paths_safe(void)
189 {
190 	asm volatile (
191 	"call ldind_subprog;"
192 	"r0 = 0;"
193 	"exit;"
194 	::: __clobber_all);
195 }
196 
197 __naked __noinline __used
198 static int ld_abs_callback(void)
199 {
200 	asm volatile (
201 	"r6 = *(u64 *)(r2 + 0);"
202 	".8byte %[ld_abs];"
203 	"r0 = 0;"
204 	"exit;"
205 	:
206 	: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
207 	: __clobber_all);
208 }
209 
210 SEC("socket")
211 __description("ld_abs: reject in callback")
212 __failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
213 int ld_abs_callback_reject(struct __sk_buff *skb)
214 {
215 	bpf_loop(1, ld_abs_callback, &skb, 0);
216 	return 0;
217 }
218 
219 __naked __noinline __used
220 static int ld_ind_callback_subprog(void)
221 {
222 	asm volatile (
223 	"r6 = r1;"
224 	"r7 = 0;"
225 	".8byte %[ld_ind];"
226 	"r0 = 0;"
227 	"exit;"
228 	:
229 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
230 	: __clobber_all);
231 }
232 
233 __naked __noinline __used
234 static int ld_ind_callback(void)
235 {
236 	asm volatile (
237 	"r1 = *(u64 *)(r2 + 0);"
238 	"call ld_ind_callback_subprog;"
239 	"exit;"
240 	::: __clobber_all);
241 }
242 
243 SEC("socket")
244 __description("ld_ind: reject in callback subprog")
245 __failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
246 int ld_ind_callback_subprog_reject(struct __sk_buff *skb)
247 {
248 	bpf_loop(1, ld_ind_callback, &skb, 0);
249 	return 0;
250 }
251 
252 static __noinline int ld_ind_global_static(struct __sk_buff *skb)
253 {
254 	asm volatile (
255 	"r6 = %[skb];"
256 	"r7 = 0;"
257 	".8byte %[ld_ind];"
258 	:
259 	: [skb] "r"(skb),
260 	  __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
261 	: __clobber_common, "r6", "r7");
262 	return skb->mark;
263 }
264 
265 __noinline int ld_ind_global(struct __sk_buff *skb)
266 {
267 	return ld_ind_global_static(skb);
268 }
269 
270 static int ld_ind_global_callback(__u32 index, struct __sk_buff **ctx)
271 {
272 	ld_ind_global(*ctx);
273 	return 0;
274 }
275 
276 SEC("socket")
277 __description("ld_ind: reject in callback global subprog")
278 __failure __msg("cannot use BPF_LD_[ABS|IND] within callback")
279 int ld_ind_global_callback_reject(struct __sk_buff *skb)
280 {
281 	bpf_loop(1, ld_ind_global_callback, &skb, 0);
282 	return 0;
283 }
284 
285 SEC("socket")
286 __description("ld_ind: allow in non-callback global subprog")
287 __success
288 int ld_ind_global_subprog_ok(struct __sk_buff *skb)
289 {
290 	return ld_ind_global(skb);
291 }
292 
293 /*
294  * ld_{abs,ind} in subprogs require scalar (int) return type in BTF.
295  * A test with void return must be rejected.
296  */
297 __naked __noinline __used
298 static void ldabs_void_subprog(void)
299 {
300 	asm volatile (
301 	"r6 = r1;"
302 	".8byte %[ld_abs];"
303 	"r0 = 1;"
304 	"exit;"
305 	:
306 	: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
307 	: __clobber_all);
308 }
309 
310 SEC("socket")
311 __description("ld_abs: reject void return subprog")
312 __failure __msg("LD_ABS is only allowed in functions that return 'int'")
313 __naked void ld_abs_void_subprog_reject(void)
314 {
315 	asm volatile (
316 	"call ldabs_void_subprog;"
317 	"r0 = 0;"
318 	"exit;"
319 	::: __clobber_all);
320 }
321 
322 __naked __noinline __used
323 static void ldind_void_subprog(void)
324 {
325 	asm volatile (
326 	"r6 = r1;"
327 	"r7 = 0;"
328 	".8byte %[ld_ind];"
329 	"r0 = 1;"
330 	"exit;"
331 	:
332 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
333 	: __clobber_all);
334 }
335 
336 SEC("socket")
337 __description("ld_ind: reject void return subprog")
338 __failure __msg("LD_ABS is only allowed in functions that return 'int'")
339 __naked void ld_ind_void_subprog_reject(void)
340 {
341 	asm volatile (
342 	"call ldind_void_subprog;"
343 	"r0 = 0;"
344 	"exit;"
345 	::: __clobber_all);
346 }
347 
348 char _license[] SEC("license") = "GPL";
349