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