1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/cfg.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("unreachable") 11 __failure __msg("unreachable") 12 __failure_unpriv 13 __naked void unreachable(void) 14 { 15 asm volatile (" \ 16 exit; \ 17 exit; \ 18 " ::: __clobber_all); 19 } 20 21 SEC("socket") 22 __description("unreachable2") 23 __failure __msg("unreachable") 24 __failure_unpriv 25 __naked void unreachable2(void) 26 { 27 asm volatile (" \ 28 goto l0_%=; \ 29 goto l0_%=; \ 30 l0_%=: exit; \ 31 " ::: __clobber_all); 32 } 33 34 SEC("socket") 35 __description("out of range jump") 36 __failure __msg("jump out of range") 37 __failure_unpriv 38 __naked void out_of_range_jump(void) 39 { 40 asm volatile (" \ 41 goto l0_%=; \ 42 exit; \ 43 l0_%=: \ 44 " ::: __clobber_all); 45 } 46 47 SEC("socket") 48 __description("out of range jump2") 49 __failure __msg("jump out of range") 50 __failure_unpriv 51 __naked void out_of_range_jump2(void) 52 { 53 asm volatile (" \ 54 goto -2; \ 55 exit; \ 56 " ::: __clobber_all); 57 } 58 59 static __naked __noinline __used int cross_subprog_target(void) 60 { 61 asm volatile (" \ 62 r0 = 0; \ 63 exit; \ 64 " ::: __clobber_all); 65 } 66 67 SEC("socket") 68 __description("jump across subprogram boundary") 69 __failure __msg("jump out of range from insn 1") 70 __msg("jump_across_subprog_boundary @ verifier_cfg.c") 71 __naked void jump_across_subprog_boundary(void) 72 { 73 asm volatile (" \ 74 call cross_subprog_target; \ 75 goto +1; \ 76 exit; \ 77 " ::: __clobber_all); 78 } 79 80 static __naked __noinline __used int fallthrough_subprog(void) 81 { 82 asm volatile (" \ 83 r0 = 0; \ 84 " ::: __clobber_all); 85 } 86 87 SEC("socket") 88 __description("subprogram fallthrough") 89 __failure __msg("last insn is not an exit or jmp") 90 __msg("fallthrough_subprog @ verifier_cfg.c") 91 __naked void subprog_fallthrough(void) 92 { 93 asm volatile (" \ 94 call fallthrough_subprog; \ 95 exit; \ 96 " ::: __clobber_all); 97 } 98 99 SEC("socket") 100 __description("invalid DW LDSX instruction in diagnostics") 101 __failure __msg("BUG_ldx_99") 102 __log_level(2) 103 __naked void invalid_dw_ldsx(void) 104 { 105 asm volatile (" \ 106 .8byte %[ldsx_dw]; \ 107 " : 108 : __imm_insn(ldsx_dw, BPF_RAW_INSN(BPF_LDX | BPF_MEMSX | BPF_DW, BPF_REG_0, BPF_REG_0, 0, 0)) 109 : __clobber_all); 110 } 111 112 SEC("socket") 113 __description("loop (back-edge)") 114 __failure __msg("unreachable insn 1") 115 __msg_unpriv("back-edge") 116 __naked void loop_back_edge(void) 117 { 118 asm volatile (" \ 119 l0_%=: goto l0_%=; \ 120 exit; \ 121 " ::: __clobber_all); 122 } 123 124 SEC("socket") 125 __description("loop2 (back-edge)") 126 __failure __msg("unreachable insn 4") 127 __msg_unpriv("back-edge") 128 __naked void loop2_back_edge(void) 129 { 130 asm volatile (" \ 131 l0_%=: r1 = r0; \ 132 r2 = r0; \ 133 r3 = r0; \ 134 goto l0_%=; \ 135 exit; \ 136 " ::: __clobber_all); 137 } 138 139 SEC("socket") 140 __description("conditional loop") 141 __failure __msg("infinite loop detected") 142 __msg_unpriv("back-edge") 143 __naked void conditional_loop(void) 144 { 145 asm volatile (" \ 146 r0 = r1; \ 147 l0_%=: r2 = r0; \ 148 r3 = r0; \ 149 if r1 == 0 goto l0_%=; \ 150 exit; \ 151 " ::: __clobber_all); 152 } 153 154 SEC("socket") 155 __description("conditional loop (2)") 156 __success 157 __failure_unpriv __msg_unpriv("back-edge from insn 10 to 11") 158 __naked void conditional_loop2(void) 159 { 160 asm volatile (" \ 161 r9 = 2 ll; \ 162 r3 = 0x20 ll; \ 163 r4 = 0x35 ll; \ 164 r8 = r4; \ 165 goto l1_%=; \ 166 l0_%=: r9 -= r3; \ 167 r9 -= r4; \ 168 r9 -= r8; \ 169 l1_%=: r8 += r4; \ 170 if r8 < 0x64 goto l0_%=; \ 171 r0 = r9; \ 172 exit; \ 173 " ::: __clobber_all); 174 } 175 176 SEC("socket") 177 __description("unconditional loop after conditional jump") 178 __failure __msg("infinite loop detected") 179 __failure_unpriv __msg_unpriv("back-edge from insn 3 to 2") 180 __naked void uncond_loop_after_cond_jmp(void) 181 { 182 asm volatile (" \ 183 r0 = 0; \ 184 if r0 > 0 goto l1_%=; \ 185 l0_%=: r0 = 1; \ 186 goto l0_%=; \ 187 l1_%=: exit; \ 188 " ::: __clobber_all); 189 } 190 191 192 __naked __noinline __used 193 static unsigned long never_ending_subprog() 194 { 195 asm volatile (" \ 196 r0 = r1; \ 197 goto -1; \ 198 " ::: __clobber_all); 199 } 200 201 SEC("socket") 202 __description("unconditional loop after conditional jump") 203 /* infinite loop is detected *after* check_cfg() */ 204 __failure __msg("infinite loop detected") 205 __naked void uncond_loop_in_subprog_after_cond_jmp(void) 206 { 207 asm volatile (" \ 208 r0 = 0; \ 209 if r0 > 0 goto l1_%=; \ 210 l0_%=: r0 += 1; \ 211 call never_ending_subprog; \ 212 l1_%=: exit; \ 213 " ::: __clobber_all); 214 } 215 216 struct { 217 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 218 __uint(max_entries, 1); 219 __type(key, __u32); 220 __type(value, __u32); 221 } map_array SEC(".maps"); 222 223 extern void bpf_throw(__u64 cookie) __ksym; 224 225 /* 226 * The subprogram leaves through the tail call or the exception and never 227 * reaches an exit insn, so the hidden edge of the tail call has no exit to 228 * point at. The stack access makes the liveness pass walk the subprogram, 229 * and the loop compiles to a single jump back onto itself. 230 */ 231 __noinline __used 232 static int tail_call_or_throw_subprog(void *ctx) 233 { 234 volatile __u32 idx = 0; 235 236 bpf_tail_call(ctx, &map_array, idx); 237 bpf_throw(0); 238 for (;;) {} 239 return 0; 240 } 241 242 SEC("tc") 243 __description("tail call in a subprogram without an exit") 244 __arch_x86_64 245 __arch_arm64 246 __success 247 __naked void tail_call_in_subprog_without_exit(void) 248 { 249 asm volatile (" \ 250 call tail_call_or_throw_subprog; \ 251 exit; \ 252 " ::: __clobber_all); 253 } 254 255 char _license[] SEC("license") = "GPL"; 256