File Index
Files and directories
Sign in
FILE

verifier_live_stack.c

File preview with syntax highlighting.

linux-6.19.14/tools/testing/selftests/bpf/progs/verifier_live_stack.c 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.