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
ind_check_calling_conv_r1(void)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
ind_check_calling_conv_r2(void)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
ind_check_calling_conv_r3(void)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
ind_check_calling_conv_r4(void)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
ind_check_calling_conv_r5(void)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)
ind_check_calling_conv_r7(void)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
ldabs_subprog(void)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
ldind_subprog(void)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")
ld_abs_subprog_early_exit(void)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")
ld_ind_subprog_early_exit(void)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
ld_abs_subprog_both_paths_safe(void)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
ld_ind_subprog_both_paths_safe(void)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
ld_abs_callback(void)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")
ld_abs_callback_reject(struct __sk_buff * skb)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
ld_ind_callback_subprog(void)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
ld_ind_callback(void)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")
ld_ind_callback_subprog_reject(struct __sk_buff * skb)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
ld_ind_global_static(struct __sk_buff * skb)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
ld_ind_global(struct __sk_buff * skb)265 __noinline int ld_ind_global(struct __sk_buff *skb)
266 {
267 return ld_ind_global_static(skb);
268 }
269
ld_ind_global_callback(__u32 index,struct __sk_buff ** ctx)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")
ld_ind_global_callback_reject(struct __sk_buff * skb)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
ld_ind_global_subprog_ok(struct __sk_buff * skb)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
ldabs_void_subprog(void)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'")
ld_abs_void_subprog_reject(void)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
ldind_void_subprog(void)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'")
ld_ind_void_subprog_reject(void)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