xref: /linux/tools/testing/selftests/bpf/progs/verifier_ld_ind.c (revision c4dde411bc366f568dbe33366253bbfea049e8ea)
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 /*
198  * ld_{abs,ind} in subprogs require scalar (int) return type in BTF.
199  * A test with void return must be rejected.
200  */
201 __naked __noinline __used
202 static void ldabs_void_subprog(void)
203 {
204 	asm volatile (
205 	"r6 = r1;"
206 	".8byte %[ld_abs];"
207 	"r0 = 1;"
208 	"exit;"
209 	:
210 	: __imm_insn(ld_abs, BPF_LD_ABS(BPF_W, 0))
211 	: __clobber_all);
212 }
213 
214 SEC("socket")
215 __description("ld_abs: reject void return subprog")
216 __failure __msg("LD_ABS is only allowed in functions that return 'int'")
217 __naked void ld_abs_void_subprog_reject(void)
218 {
219 	asm volatile (
220 	"call ldabs_void_subprog;"
221 	"r0 = 0;"
222 	"exit;"
223 	::: __clobber_all);
224 }
225 
226 __naked __noinline __used
227 static void ldind_void_subprog(void)
228 {
229 	asm volatile (
230 	"r6 = r1;"
231 	"r7 = 0;"
232 	".8byte %[ld_ind];"
233 	"r0 = 1;"
234 	"exit;"
235 	:
236 	: __imm_insn(ld_ind, BPF_LD_IND(BPF_W, BPF_REG_7, 0))
237 	: __clobber_all);
238 }
239 
240 SEC("socket")
241 __description("ld_ind: reject void return subprog")
242 __failure __msg("LD_ABS is only allowed in functions that return 'int'")
243 __naked void ld_ind_void_subprog_reject(void)
244 {
245 	asm volatile (
246 	"call ldind_void_subprog;"
247 	"r0 = 0;"
248 	"exit;"
249 	::: __clobber_all);
250 }
251 
252 char _license[] SEC("license") = "GPL";
253