FILE
verifier_live_stack.c
File preview with syntax highlighting.
linux-6.19.14/tools/testing/selftests/bpf/progs/verifier_live_stack.c
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 8struct { 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 15SEC("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 34SEC("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 59SEC("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 84SEC("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 111SEC("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 127static __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 136SEC("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 168static __used __naked void read_first_param(void) 169{ 170 asm volatile ( 171 "r0 = *(u64 *)(r1 + 0);" 172 "exit;" 173 ::: __clobber_all); 174} 175 176SEC("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 213static __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 227SEC("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 255static __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 */ 286SEC("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 296struct { 297 __uint(type, BPF_MAP_TYPE_PROG_ARRAY); 298 __uint(max_entries, 1); 299 __type(key, __u32); 300 __type(value, __u32); 301} map_array SEC(".maps"); 302 303SEC("socket") 304__failure __msg("invalid read from stack R2 off=-1024 size=8") 305__flag(BPF_F_TEST_STATE_FREQ) 306__naked unsigned long caller_stack_write_tail_call(void) 307{ 308 asm volatile ( 309 "r6 = r1;" 310 "*(u64 *)(r10 - 8) = -8;" 311 "call %[bpf_get_prandom_u32];" 312 "if r0 != 42 goto 1f;" 313 "goto 2f;" 314 "1:" 315 "*(u64 *)(r10 - 8) = -1024;" 316 "2:" 317 "r1 = r6;" 318 "r2 = r10;" 319 "r2 += -8;" 320 "call write_tail_call;" 321 "r1 = *(u64 *)(r10 - 8);" 322 "r2 = r10;" 323 "r2 += r1;" 324 "r0 = *(u64 *)(r2 + 0);" 325 "exit;" 326 :: __imm(bpf_get_prandom_u32) 327 : __clobber_all); 328} 329 330static __used __naked unsigned long write_tail_call(void) 331{ 332 asm volatile ( 333 "r6 = r2;" 334 "r2 = %[map_array] ll;" 335 "r3 = 0;" 336 "call %[bpf_tail_call];" 337 "*(u64 *)(r6 + 0) = -16;" 338 "r0 = 0;" 339 "exit;" 340 : 341 : __imm(bpf_tail_call), 342 __imm_addr(map_array) 343 : __clobber_all); 344}
~/
Read-only file preview.