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