1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2025 Meta Platforms, Inc. and affiliates. */ 3 4 #include <linux/bpf.h> 5 #include <bpf/bpf_helpers.h> 6 #include "bpf_misc.h" 7 8 struct { 9 __uint(type, BPF_MAP_TYPE_HASH); 10 __uint(max_entries, 1); 11 __type(key, int); 12 __type(value, long long); 13 } map SEC(".maps"); 14 15 SEC("socket") 16 __log_level(2) 17 __msg("(0) frame 0 insn 2 +written -8") 18 __msg("(0) frame 0 insn 1 +live -24") 19 __msg("(0) frame 0 insn 1 +written -8") 20 __msg("(0) frame 0 insn 0 +live -8,-24") 21 __msg("(0) frame 0 insn 0 +written -8") 22 __msg("(0) live stack update done in 2 iterations") 23 __naked void simple_read_simple_write(void) 24 { 25 asm volatile ( 26 "r1 = *(u64 *)(r10 - 8);" 27 "r2 = *(u64 *)(r10 - 24);" 28 "*(u64 *)(r10 - 8) = r1;" 29 "r0 = 0;" 30 "exit;" 31 ::: __clobber_all); 32 } 33 34 SEC("socket") 35 __log_level(2) 36 __msg("(0) frame 0 insn 1 +live -8") 37 __not_msg("(0) frame 0 insn 1 +written") 38 __msg("(0) live stack update done in 2 iterations") 39 __msg("(0) frame 0 insn 1 +live -16") 40 __msg("(0) frame 0 insn 1 +written -32") 41 __msg("(0) live stack update done in 2 iterations") 42 __naked void read_write_join(void) 43 { 44 asm volatile ( 45 "call %[bpf_get_prandom_u32];" 46 "if r0 > 42 goto 1f;" 47 "r0 = *(u64 *)(r10 - 8);" 48 "*(u64 *)(r10 - 32) = r0;" 49 "*(u64 *)(r10 - 40) = r0;" 50 "exit;" 51 "1:" 52 "r0 = *(u64 *)(r10 - 16);" 53 "*(u64 *)(r10 - 32) = r0;" 54 "exit;" 55 :: __imm(bpf_get_prandom_u32) 56 : __clobber_all); 57 } 58 59 SEC("socket") 60 __log_level(2) 61 __msg("2: (25) if r0 > 0x2a goto pc+1") 62 __msg("7: (95) exit") 63 __msg("(0) frame 0 insn 2 +written -16") 64 __msg("(0) live stack update done in 2 iterations") 65 __msg("7: (95) exit") 66 __not_msg("(0) frame 0 insn 2") 67 __msg("(0) live stack update done in 1 iterations") 68 __naked void must_write_not_same_slot(void) 69 { 70 asm volatile ( 71 "call %[bpf_get_prandom_u32];" 72 "r1 = -8;" 73 "if r0 > 42 goto 1f;" 74 "r1 = -16;" 75 "1:" 76 "r2 = r10;" 77 "r2 += r1;" 78 "*(u64 *)(r2 + 0) = r0;" 79 "exit;" 80 :: __imm(bpf_get_prandom_u32) 81 : __clobber_all); 82 } 83 84 SEC("socket") 85 __log_level(2) 86 __msg("(0) frame 0 insn 0 +written -8,-16") 87 __msg("(0) live stack update done in 2 iterations") 88 __msg("(0) frame 0 insn 0 +written -8") 89 __msg("(0) live stack update done in 2 iterations") 90 __naked void must_write_not_same_type(void) 91 { 92 asm volatile ( 93 "*(u64*)(r10 - 8) = 0;" 94 "r2 = r10;" 95 "r2 += -8;" 96 "r1 = %[map] ll;" 97 "call %[bpf_map_lookup_elem];" 98 "if r0 != 0 goto 1f;" 99 "r0 = r10;" 100 "r0 += -16;" 101 "1:" 102 "*(u64 *)(r0 + 0) = 42;" 103 "exit;" 104 : 105 : __imm(bpf_get_prandom_u32), 106 __imm(bpf_map_lookup_elem), 107 __imm_addr(map) 108 : __clobber_all); 109 } 110 111 SEC("socket") 112 __log_level(2) 113 __msg("(2,4) frame 0 insn 4 +written -8") 114 __msg("(2,4) live stack update done in 2 iterations") 115 __msg("(0) frame 0 insn 2 +written -8") 116 __msg("(0) live stack update done in 2 iterations") 117 __naked void caller_stack_write(void) 118 { 119 asm volatile ( 120 "r1 = r10;" 121 "r1 += -8;" 122 "call write_first_param;" 123 "exit;" 124 ::: __clobber_all); 125 } 126 127 static __used __naked void write_first_param(void) 128 { 129 asm volatile ( 130 "*(u64 *)(r1 + 0) = 7;" 131 "r0 = 0;" 132 "exit;" 133 ::: __clobber_all); 134 } 135 136 SEC("socket") 137 __log_level(2) 138 /* caller_stack_read() function */ 139 __msg("2: .12345.... (85) call pc+4") 140 __msg("5: .12345.... (85) call pc+1") 141 __msg("6: 0......... (95) exit") 142 /* read_first_param() function */ 143 __msg("7: .1........ (79) r0 = *(u64 *)(r1 +0)") 144 __msg("8: 0......... (95) exit") 145 /* update for callsite at (2) */ 146 __msg("(2,7) frame 0 insn 7 +live -8") 147 __msg("(2,7) live stack update done in 2 iterations") 148 __msg("(0) frame 0 insn 2 +live -8") 149 __msg("(0) live stack update done in 2 iterations") 150 /* update for callsite at (5) */ 151 __msg("(5,7) frame 0 insn 7 +live -16") 152 __msg("(5,7) live stack update done in 2 iterations") 153 __msg("(0) frame 0 insn 5 +live -16") 154 __msg("(0) live stack update done in 2 iterations") 155 __naked void caller_stack_read(void) 156 { 157 asm volatile ( 158 "r1 = r10;" 159 "r1 += -8;" 160 "call read_first_param;" 161 "r1 = r10;" 162 "r1 += -16;" 163 "call read_first_param;" 164 "exit;" 165 ::: __clobber_all); 166 } 167 168 static __used __naked void read_first_param(void) 169 { 170 asm volatile ( 171 "r0 = *(u64 *)(r1 + 0);" 172 "exit;" 173 ::: __clobber_all); 174 } 175 176 SEC("socket") 177 __flag(BPF_F_TEST_STATE_FREQ) 178 __log_level(2) 179 /* read_first_param2() function */ 180 __msg(" 9: .1........ (79) r0 = *(u64 *)(r1 +0)") 181 __msg("10: .......... (b7) r0 = 0") 182 __msg("11: 0......... (05) goto pc+0") 183 __msg("12: 0......... (95) exit") 184 /* 185 * The purpose of the test is to check that checkpoint in 186 * read_first_param2() stops path traversal. This will only happen if 187 * verifier understands that fp[0]-8 at insn (12) is not alive. 188 */ 189 __msg("12: safe") 190 __msg("processed 20 insns") 191 __naked void caller_stack_pruning(void) 192 { 193 asm volatile ( 194 "call %[bpf_get_prandom_u32];" 195 "if r0 == 42 goto 1f;" 196 "r0 = %[map] ll;" 197 "1:" 198 "*(u64 *)(r10 - 8) = r0;" 199 "r1 = r10;" 200 "r1 += -8;" 201 /* 202 * fp[0]-8 is either pointer to map or a scalar, 203 * preventing state pruning at checkpoint created for call. 204 */ 205 "call read_first_param2;" 206 "exit;" 207 : 208 : __imm(bpf_get_prandom_u32), 209 __imm_addr(map) 210 : __clobber_all); 211 } 212 213 static __used __naked void read_first_param2(void) 214 { 215 asm volatile ( 216 "r0 = *(u64 *)(r1 + 0);" 217 "r0 = 0;" 218 /* 219 * Checkpoint at goto +0 should fire, 220 * as caller stack fp[0]-8 is not alive at this point. 221 */ 222 "goto +0;" 223 "exit;" 224 ::: __clobber_all); 225 } 226 227 SEC("socket") 228 __flag(BPF_F_TEST_STATE_FREQ) 229 __failure 230 __msg("R1 type=scalar expected=map_ptr") 231 __naked void caller_stack_pruning_callback(void) 232 { 233 asm volatile ( 234 "r0 = %[map] ll;" 235 "*(u64 *)(r10 - 8) = r0;" 236 "r1 = 2;" 237 "r2 = loop_cb ll;" 238 "r3 = r10;" 239 "r3 += -8;" 240 "r4 = 0;" 241 /* 242 * fp[0]-8 is either pointer to map or a scalar, 243 * preventing state pruning at checkpoint created for call. 244 */ 245 "call %[bpf_loop];" 246 "r0 = 42;" 247 "exit;" 248 : 249 : __imm(bpf_get_prandom_u32), 250 __imm(bpf_loop), 251 __imm_addr(map) 252 : __clobber_all); 253 } 254 255 static __used __naked void loop_cb(void) 256 { 257 asm volatile ( 258 /* 259 * Checkpoint at function entry should not fire, as caller 260 * stack fp[0]-8 is alive at this point. 261 */ 262 "r6 = r2;" 263 "r1 = *(u64 *)(r6 + 0);" 264 "*(u64*)(r10 - 8) = 7;" 265 "r2 = r10;" 266 "r2 += -8;" 267 "call %[bpf_map_lookup_elem];" 268 /* 269 * This should stop verifier on a second loop iteration, 270 * but only if verifier correctly maintains that fp[0]-8 271 * is still alive. 272 */ 273 "*(u64 *)(r6 + 0) = 0;" 274 "r0 = 0;" 275 "exit;" 276 : 277 : __imm(bpf_map_lookup_elem), 278 __imm(bpf_get_prandom_u32) 279 : __clobber_all); 280 } 281 282 /* 283 * Because of a bug in verifier.c:compute_postorder() 284 * the program below overflowed traversal queue in that function. 285 */ 286 SEC("socket") 287 __naked void syzbot_postorder_bug1(void) 288 { 289 asm volatile ( 290 "r0 = 0;" 291 "if r0 != 0 goto -1;" 292 "exit;" 293 ::: __clobber_all); 294 } 295