xref: /linux/tools/testing/selftests/bpf/progs/verifier_live_stack.c (revision fdcecdff905cf712279d3ba72ec8ac7bc02be7ff)
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