1 // SPDX-License-Identifier: GPL-2.0 2 /* Converted from tools/testing/selftests/bpf/verifier/search_pruning.c */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 #define MAX_ENTRIES 11 9 10 struct test_val { 11 unsigned int index; 12 int foo[MAX_ENTRIES]; 13 }; 14 15 struct { 16 __uint(type, BPF_MAP_TYPE_HASH); 17 __uint(max_entries, 1); 18 __type(key, long long); 19 __type(value, struct test_val); 20 } map_hash_48b SEC(".maps"); 21 22 struct { 23 __uint(type, BPF_MAP_TYPE_HASH); 24 __uint(max_entries, 1); 25 __type(key, long long); 26 __type(value, long long); 27 } map_hash_8b SEC(".maps"); 28 29 SEC("socket") 30 __description("pointer/scalar confusion in state equality check (way 1)") 31 __success __failure_unpriv __msg_unpriv("R0 leaks addr as return value") 32 __retval(POINTER_VALUE) 33 __naked void state_equality_check_way_1(void) 34 { 35 asm volatile (" \ 36 r1 = 0; \ 37 *(u64*)(r10 - 8) = r1; \ 38 r2 = r10; \ 39 r2 += -8; \ 40 r1 = %[map_hash_8b] ll; \ 41 call %[bpf_map_lookup_elem]; \ 42 if r0 == 0 goto l0_%=; \ 43 r0 = *(u64*)(r0 + 0); \ 44 goto l1_%=; \ 45 l0_%=: r0 = r10; \ 46 l1_%=: goto l2_%=; \ 47 l2_%=: exit; \ 48 " : 49 : __imm(bpf_map_lookup_elem), 50 __imm_addr(map_hash_8b) 51 : __clobber_all); 52 } 53 54 SEC("socket") 55 __description("pointer/scalar confusion in state equality check (way 2)") 56 __success __failure_unpriv __msg_unpriv("R0 leaks addr as return value") 57 __retval(POINTER_VALUE) 58 __naked void state_equality_check_way_2(void) 59 { 60 asm volatile (" \ 61 r1 = 0; \ 62 *(u64*)(r10 - 8) = r1; \ 63 r2 = r10; \ 64 r2 += -8; \ 65 r1 = %[map_hash_8b] ll; \ 66 call %[bpf_map_lookup_elem]; \ 67 if r0 != 0 goto l0_%=; \ 68 r0 = r10; \ 69 goto l1_%=; \ 70 l0_%=: r0 = *(u64*)(r0 + 0); \ 71 l1_%=: exit; \ 72 " : 73 : __imm(bpf_map_lookup_elem), 74 __imm_addr(map_hash_8b) 75 : __clobber_all); 76 } 77 78 SEC("lwt_in") 79 __description("liveness pruning and write screening") 80 __failure __msg("R0 !read_ok") 81 __naked void liveness_pruning_and_write_screening(void) 82 { 83 asm volatile (" \ 84 /* Get an unknown value */ \ 85 r2 = *(u32*)(r1 + 0); \ 86 /* branch conditions teach us nothing about R2 */\ 87 if r2 >= 0 goto l0_%=; \ 88 r0 = 0; \ 89 l0_%=: if r2 >= 0 goto l1_%=; \ 90 r0 = 0; \ 91 l1_%=: exit; \ 92 " ::: __clobber_all); 93 } 94 95 SEC("socket") 96 __description("varlen_map_value_access pruning") 97 __failure __msg("R0 unbounded memory access") 98 __failure_unpriv __msg_unpriv("R0 leaks addr") 99 __flag(BPF_F_ANY_ALIGNMENT) 100 __naked void varlen_map_value_access_pruning(void) 101 { 102 asm volatile (" \ 103 r1 = 0; \ 104 *(u64*)(r10 - 8) = r1; \ 105 r2 = r10; \ 106 r2 += -8; \ 107 r1 = %[map_hash_48b] ll; \ 108 call %[bpf_map_lookup_elem]; \ 109 if r0 == 0 goto l0_%=; \ 110 r1 = *(u64*)(r0 + 0); \ 111 w2 = %[max_entries]; \ 112 if r2 s> r1 goto l1_%=; \ 113 w1 = 0; \ 114 l1_%=: w1 <<= 2; \ 115 r0 += r1; \ 116 goto l2_%=; \ 117 l2_%=: r1 = %[test_val_foo]; \ 118 *(u64*)(r0 + 0) = r1; \ 119 l0_%=: exit; \ 120 " : 121 : __imm(bpf_map_lookup_elem), 122 __imm_addr(map_hash_48b), 123 __imm_const(max_entries, MAX_ENTRIES), 124 __imm_const(test_val_foo, offsetof(struct test_val, foo)) 125 : __clobber_all); 126 } 127 128 SEC("tracepoint") 129 __description("search pruning: all branches should be verified (nop operation)") 130 __failure __msg("R6 invalid mem access 'scalar'") 131 __naked void should_be_verified_nop_operation(void) 132 { 133 asm volatile (" \ 134 r2 = r10; \ 135 r2 += -8; \ 136 r1 = 0; \ 137 *(u64*)(r2 + 0) = r1; \ 138 r1 = %[map_hash_8b] ll; \ 139 call %[bpf_map_lookup_elem]; \ 140 if r0 == 0 goto l0_%=; \ 141 r3 = *(u64*)(r0 + 0); \ 142 if r3 == 0xbeef goto l1_%=; \ 143 r4 = 0; \ 144 goto l2_%=; \ 145 l1_%=: r4 = 1; \ 146 l2_%=: *(u64*)(r10 - 16) = r4; \ 147 call %[bpf_ktime_get_ns]; \ 148 r5 = *(u64*)(r10 - 16); \ 149 if r5 == 0 goto l0_%=; \ 150 r6 = 0; \ 151 r1 = 0xdead; \ 152 *(u64*)(r6 + 0) = r1; \ 153 l0_%=: exit; \ 154 " : 155 : __imm(bpf_ktime_get_ns), 156 __imm(bpf_map_lookup_elem), 157 __imm_addr(map_hash_8b) 158 : __clobber_all); 159 } 160 161 SEC("socket") 162 __description("search pruning: all branches should be verified (invalid stack access)") 163 /* in privileged mode reads from uninitialized stack locations are permitted */ 164 __success __failure_unpriv 165 __msg_unpriv("invalid read from stack off -16+0 size 8") 166 __retval(0) 167 __naked void be_verified_invalid_stack_access(void) 168 { 169 asm volatile (" \ 170 r2 = r10; \ 171 r2 += -8; \ 172 r1 = 0; \ 173 *(u64*)(r2 + 0) = r1; \ 174 r1 = %[map_hash_8b] ll; \ 175 call %[bpf_map_lookup_elem]; \ 176 if r0 == 0 goto l0_%=; \ 177 r3 = *(u64*)(r0 + 0); \ 178 r4 = 0; \ 179 if r3 == 0xbeef goto l1_%=; \ 180 *(u64*)(r10 - 16) = r4; \ 181 goto l2_%=; \ 182 l1_%=: *(u64*)(r10 - 24) = r4; \ 183 l2_%=: call %[bpf_ktime_get_ns]; \ 184 r5 = *(u64*)(r10 - 16); \ 185 l0_%=: exit; \ 186 " : 187 : __imm(bpf_ktime_get_ns), 188 __imm(bpf_map_lookup_elem), 189 __imm_addr(map_hash_8b) 190 : __clobber_all); 191 } 192 193 SEC("tracepoint") 194 __description("precision tracking for u32 spill/fill") 195 __failure __msg("R0 min value is outside of the allowed memory range") 196 __naked void tracking_for_u32_spill_fill(void) 197 { 198 asm volatile (" \ 199 r7 = r1; \ 200 call %[bpf_get_prandom_u32]; \ 201 w6 = 32; \ 202 if r0 == 0 goto l0_%=; \ 203 w6 = 4; \ 204 l0_%=: /* Additional insns to introduce a pruning point. */\ 205 call %[bpf_get_prandom_u32]; \ 206 r3 = 0; \ 207 r3 = 0; \ 208 if r0 == 0 goto l1_%=; \ 209 r3 = 0; \ 210 l1_%=: /* u32 spill/fill */ \ 211 *(u32*)(r10 - 8) = r6; \ 212 r8 = *(u32*)(r10 - 8); \ 213 /* out-of-bound map value access for r6=32 */ \ 214 r1 = 0; \ 215 *(u64*)(r10 - 16) = r1; \ 216 r2 = r10; \ 217 r2 += -16; \ 218 r1 = %[map_hash_8b] ll; \ 219 call %[bpf_map_lookup_elem]; \ 220 if r0 == 0 goto l2_%=; \ 221 r0 += r8; \ 222 r1 = *(u32*)(r0 + 0); \ 223 l2_%=: r0 = 0; \ 224 exit; \ 225 " : 226 : __imm(bpf_get_prandom_u32), 227 __imm(bpf_map_lookup_elem), 228 __imm_addr(map_hash_8b) 229 : __clobber_all); 230 } 231 232 SEC("tracepoint") 233 __description("precision tracking for u32 spills, u64 fill") 234 __failure __msg("div by zero") 235 __naked void for_u32_spills_u64_fill(void) 236 { 237 asm volatile (" \ 238 call %[bpf_get_prandom_u32]; \ 239 r6 = r0; \ 240 w7 = 0xffffffff; \ 241 /* Additional insns to introduce a pruning point. */\ 242 r3 = 1; \ 243 r3 = 1; \ 244 r3 = 1; \ 245 r3 = 1; \ 246 call %[bpf_get_prandom_u32]; \ 247 if r0 == 0 goto l0_%=; \ 248 r3 = 1; \ 249 l0_%=: w3 /= 0; \ 250 /* u32 spills, u64 fill */ \ 251 *(u32*)(r10 - 4) = r6; \ 252 *(u32*)(r10 - 8) = r7; \ 253 r8 = *(u64*)(r10 - 8); \ 254 /* if r8 != X goto pc+1 r8 known in fallthrough branch */\ 255 if r8 != 0xffffffff goto l1_%=; \ 256 r3 = 1; \ 257 l1_%=: /* if r8 == X goto pc+1 condition always true on first\ 258 * traversal, so starts backtracking to mark r8 as requiring\ 259 * precision. r7 marked as needing precision. r6 not marked\ 260 * since it's not tracked. \ 261 */ \ 262 if r8 == 0xffffffff goto l2_%=; \ 263 /* fails if r8 correctly marked unknown after fill. */\ 264 w3 /= 0; \ 265 l2_%=: r0 = 0; \ 266 exit; \ 267 " : 268 : __imm(bpf_get_prandom_u32) 269 : __clobber_all); 270 } 271 272 SEC("socket") 273 __description("allocated_stack") 274 __success __msg("processed 15 insns") 275 __success_unpriv __msg_unpriv("") __log_level(1) __retval(0) 276 __naked void allocated_stack(void) 277 { 278 asm volatile (" \ 279 r6 = r1; \ 280 call %[bpf_get_prandom_u32]; \ 281 r7 = r0; \ 282 if r0 == 0 goto l0_%=; \ 283 r0 = 0; \ 284 *(u64*)(r10 - 8) = r6; \ 285 r6 = *(u64*)(r10 - 8); \ 286 *(u8*)(r10 - 9) = r7; \ 287 r7 = *(u8*)(r10 - 9); \ 288 l0_%=: if r0 != 0 goto l1_%=; \ 289 l1_%=: if r0 != 0 goto l2_%=; \ 290 l2_%=: if r0 != 0 goto l3_%=; \ 291 l3_%=: if r0 != 0 goto l4_%=; \ 292 l4_%=: exit; \ 293 " : 294 : __imm(bpf_get_prandom_u32) 295 : __clobber_all); 296 } 297 298 /* The test performs a conditional 64-bit write to a stack location 299 * fp[-8], this is followed by an unconditional 8-bit write to fp[-8], 300 * then data is read from fp[-8]. This sequence is unsafe. 301 * 302 * The test would be mistakenly marked as safe w/o dst register parent 303 * preservation in verifier.c:copy_register_state() function. 304 * 305 * Note the usage of BPF_F_TEST_STATE_FREQ to force creation of the 306 * checkpoint state after conditional 64-bit assignment. 307 */ 308 309 SEC("socket") 310 __description("write tracking and register parent chain bug") 311 /* in privileged mode reads from uninitialized stack locations are permitted */ 312 __success __failure_unpriv 313 __msg_unpriv("invalid read from stack off -8+1 size 8") 314 __retval(0) __flag(BPF_F_TEST_STATE_FREQ) 315 __naked void and_register_parent_chain_bug(void) 316 { 317 asm volatile (" \ 318 /* r6 = ktime_get_ns() */ \ 319 call %[bpf_ktime_get_ns]; \ 320 r6 = r0; \ 321 /* r0 = ktime_get_ns() */ \ 322 call %[bpf_ktime_get_ns]; \ 323 /* if r0 > r6 goto +1 */ \ 324 if r0 > r6 goto l0_%=; \ 325 /* *(u64 *)(r10 - 8) = 0xdeadbeef */ \ 326 r0 = 0xdeadbeef; \ 327 *(u64*)(r10 - 8) = r0; \ 328 l0_%=: r1 = 42; \ 329 *(u8*)(r10 - 8) = r1; \ 330 r2 = *(u64*)(r10 - 8); \ 331 /* exit(0) */ \ 332 r0 = 0; \ 333 exit; \ 334 " : 335 : __imm(bpf_ktime_get_ns) 336 : __clobber_all); 337 } 338 339 char _license[] SEC("license") = "GPL"; 340