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