1*57400dccSAndrii Nakryiko // SPDX-License-Identifier: GPL-2.0
2*57400dccSAndrii Nakryiko /* Copyright (c) 2022 Facebook */
3*57400dccSAndrii Nakryiko 
4*57400dccSAndrii Nakryiko #include <errno.h>
5*57400dccSAndrii Nakryiko #include <string.h>
6*57400dccSAndrii Nakryiko #include <linux/bpf.h>
7*57400dccSAndrii Nakryiko #include <bpf/bpf_helpers.h>
8*57400dccSAndrii Nakryiko #include "bpf_misc.h"
9*57400dccSAndrii Nakryiko 
10*57400dccSAndrii Nakryiko char _license[] SEC("license") = "GPL";
11*57400dccSAndrii Nakryiko 
12*57400dccSAndrii Nakryiko #define ITER_HELPERS						\
13*57400dccSAndrii Nakryiko 	  __imm(bpf_iter_num_new),				\
14*57400dccSAndrii Nakryiko 	  __imm(bpf_iter_num_next),				\
15*57400dccSAndrii Nakryiko 	  __imm(bpf_iter_num_destroy)
16*57400dccSAndrii Nakryiko 
17*57400dccSAndrii Nakryiko SEC("?raw_tp")
18*57400dccSAndrii Nakryiko __success
force_clang_to_emit_btf_for_externs(void * ctx)19*57400dccSAndrii Nakryiko int force_clang_to_emit_btf_for_externs(void *ctx)
20*57400dccSAndrii Nakryiko {
21*57400dccSAndrii Nakryiko 	/* we need this as a workaround to enforce compiler emitting BTF
22*57400dccSAndrii Nakryiko 	 * information for bpf_iter_num_{new,next,destroy}() kfuncs,
23*57400dccSAndrii Nakryiko 	 * as, apparently, it doesn't emit it for symbols only referenced from
24*57400dccSAndrii Nakryiko 	 * assembly (or cleanup attribute, for that matter, as well)
25*57400dccSAndrii Nakryiko 	 */
26*57400dccSAndrii Nakryiko 	bpf_repeat(0);
27*57400dccSAndrii Nakryiko 
28*57400dccSAndrii Nakryiko 	return 0;
29*57400dccSAndrii Nakryiko }
30*57400dccSAndrii Nakryiko 
31*57400dccSAndrii Nakryiko SEC("?raw_tp")
32*57400dccSAndrii Nakryiko __success __log_level(2)
33*57400dccSAndrii Nakryiko __msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)")
create_and_destroy(void * ctx)34*57400dccSAndrii Nakryiko int create_and_destroy(void *ctx)
35*57400dccSAndrii Nakryiko {
36*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
37*57400dccSAndrii Nakryiko 
38*57400dccSAndrii Nakryiko 	asm volatile (
39*57400dccSAndrii Nakryiko 		/* create iterator */
40*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
41*57400dccSAndrii Nakryiko 		"r2 = 0;"
42*57400dccSAndrii Nakryiko 		"r3 = 1000;"
43*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
44*57400dccSAndrii Nakryiko 		/* destroy iterator */
45*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
46*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
47*57400dccSAndrii Nakryiko 		:
48*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
49*57400dccSAndrii Nakryiko 		: __clobber_common
50*57400dccSAndrii Nakryiko 	);
51*57400dccSAndrii Nakryiko 
52*57400dccSAndrii Nakryiko 	return 0;
53*57400dccSAndrii Nakryiko }
54*57400dccSAndrii Nakryiko 
55*57400dccSAndrii Nakryiko SEC("?raw_tp")
56*57400dccSAndrii Nakryiko __failure __msg("Unreleased reference id=1")
create_and_forget_to_destroy_fail(void * ctx)57*57400dccSAndrii Nakryiko int create_and_forget_to_destroy_fail(void *ctx)
58*57400dccSAndrii Nakryiko {
59*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
60*57400dccSAndrii Nakryiko 
61*57400dccSAndrii Nakryiko 	asm volatile (
62*57400dccSAndrii Nakryiko 		/* create iterator */
63*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
64*57400dccSAndrii Nakryiko 		"r2 = 0;"
65*57400dccSAndrii Nakryiko 		"r3 = 1000;"
66*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
67*57400dccSAndrii Nakryiko 		:
68*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
69*57400dccSAndrii Nakryiko 		: __clobber_common
70*57400dccSAndrii Nakryiko 	);
71*57400dccSAndrii Nakryiko 
72*57400dccSAndrii Nakryiko 	return 0;
73*57400dccSAndrii Nakryiko }
74*57400dccSAndrii Nakryiko 
75*57400dccSAndrii Nakryiko SEC("?raw_tp")
76*57400dccSAndrii Nakryiko __failure __msg("expected an initialized iter_num as arg #1")
destroy_without_creating_fail(void * ctx)77*57400dccSAndrii Nakryiko int destroy_without_creating_fail(void *ctx)
78*57400dccSAndrii Nakryiko {
79*57400dccSAndrii Nakryiko 	/* init with zeros to stop verifier complaining about uninit stack */
80*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
81*57400dccSAndrii Nakryiko 
82*57400dccSAndrii Nakryiko 	asm volatile (
83*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
84*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
85*57400dccSAndrii Nakryiko 		:
86*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
87*57400dccSAndrii Nakryiko 		: __clobber_common
88*57400dccSAndrii Nakryiko 	);
89*57400dccSAndrii Nakryiko 
90*57400dccSAndrii Nakryiko 	return 0;
91*57400dccSAndrii Nakryiko }
92*57400dccSAndrii Nakryiko 
93*57400dccSAndrii Nakryiko SEC("?raw_tp")
94*57400dccSAndrii Nakryiko __failure __msg("expected an initialized iter_num as arg #1")
compromise_iter_w_direct_write_fail(void * ctx)95*57400dccSAndrii Nakryiko int compromise_iter_w_direct_write_fail(void *ctx)
96*57400dccSAndrii Nakryiko {
97*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
98*57400dccSAndrii Nakryiko 
99*57400dccSAndrii Nakryiko 	asm volatile (
100*57400dccSAndrii Nakryiko 		/* create iterator */
101*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
102*57400dccSAndrii Nakryiko 		"r2 = 0;"
103*57400dccSAndrii Nakryiko 		"r3 = 1000;"
104*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
105*57400dccSAndrii Nakryiko 
106*57400dccSAndrii Nakryiko 		/* directly write over first half of iter state */
107*57400dccSAndrii Nakryiko 		"*(u64 *)(%[iter] + 0) = r0;"
108*57400dccSAndrii Nakryiko 
109*57400dccSAndrii Nakryiko 		/* (attempt to) destroy iterator */
110*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
111*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
112*57400dccSAndrii Nakryiko 		:
113*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
114*57400dccSAndrii Nakryiko 		: __clobber_common
115*57400dccSAndrii Nakryiko 	);
116*57400dccSAndrii Nakryiko 
117*57400dccSAndrii Nakryiko 	return 0;
118*57400dccSAndrii Nakryiko }
119*57400dccSAndrii Nakryiko 
120*57400dccSAndrii Nakryiko SEC("?raw_tp")
121*57400dccSAndrii Nakryiko __failure __msg("Unreleased reference id=1")
compromise_iter_w_direct_write_and_skip_destroy_fail(void * ctx)122*57400dccSAndrii Nakryiko int compromise_iter_w_direct_write_and_skip_destroy_fail(void *ctx)
123*57400dccSAndrii Nakryiko {
124*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
125*57400dccSAndrii Nakryiko 
126*57400dccSAndrii Nakryiko 	asm volatile (
127*57400dccSAndrii Nakryiko 		/* create iterator */
128*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
129*57400dccSAndrii Nakryiko 		"r2 = 0;"
130*57400dccSAndrii Nakryiko 		"r3 = 1000;"
131*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
132*57400dccSAndrii Nakryiko 
133*57400dccSAndrii Nakryiko 		/* directly write over first half of iter state */
134*57400dccSAndrii Nakryiko 		"*(u64 *)(%[iter] + 0) = r0;"
135*57400dccSAndrii Nakryiko 
136*57400dccSAndrii Nakryiko 		/* don't destroy iter, leaking ref, which should fail */
137*57400dccSAndrii Nakryiko 		:
138*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
139*57400dccSAndrii Nakryiko 		: __clobber_common
140*57400dccSAndrii Nakryiko 	);
141*57400dccSAndrii Nakryiko 
142*57400dccSAndrii Nakryiko 	return 0;
143*57400dccSAndrii Nakryiko }
144*57400dccSAndrii Nakryiko 
145*57400dccSAndrii Nakryiko SEC("?raw_tp")
146*57400dccSAndrii Nakryiko __failure __msg("expected an initialized iter_num as arg #1")
compromise_iter_w_helper_write_fail(void * ctx)147*57400dccSAndrii Nakryiko int compromise_iter_w_helper_write_fail(void *ctx)
148*57400dccSAndrii Nakryiko {
149*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
150*57400dccSAndrii Nakryiko 
151*57400dccSAndrii Nakryiko 	asm volatile (
152*57400dccSAndrii Nakryiko 		/* create iterator */
153*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
154*57400dccSAndrii Nakryiko 		"r2 = 0;"
155*57400dccSAndrii Nakryiko 		"r3 = 1000;"
156*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
157*57400dccSAndrii Nakryiko 
158*57400dccSAndrii Nakryiko 		/* overwrite 8th byte with bpf_probe_read_kernel() */
159*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
160*57400dccSAndrii Nakryiko 		"r1 += 7;"
161*57400dccSAndrii Nakryiko 		"r2 = 1;"
162*57400dccSAndrii Nakryiko 		"r3 = 0;" /* NULL */
163*57400dccSAndrii Nakryiko 		"call %[bpf_probe_read_kernel];"
164*57400dccSAndrii Nakryiko 
165*57400dccSAndrii Nakryiko 		/* (attempt to) destroy iterator */
166*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
167*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
168*57400dccSAndrii Nakryiko 		:
169*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS, __imm(bpf_probe_read_kernel)
170*57400dccSAndrii Nakryiko 		: __clobber_common
171*57400dccSAndrii Nakryiko 	);
172*57400dccSAndrii Nakryiko 
173*57400dccSAndrii Nakryiko 	return 0;
174*57400dccSAndrii Nakryiko }
175*57400dccSAndrii Nakryiko 
subprog_with_iter(void)176*57400dccSAndrii Nakryiko static __noinline void subprog_with_iter(void)
177*57400dccSAndrii Nakryiko {
178*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
179*57400dccSAndrii Nakryiko 
180*57400dccSAndrii Nakryiko 	bpf_iter_num_new(&iter, 0, 1);
181*57400dccSAndrii Nakryiko 
182*57400dccSAndrii Nakryiko 	return;
183*57400dccSAndrii Nakryiko }
184*57400dccSAndrii Nakryiko 
185*57400dccSAndrii Nakryiko SEC("?raw_tp")
186*57400dccSAndrii Nakryiko __failure
187*57400dccSAndrii Nakryiko /* ensure there was a call to subprog, which might happen without __noinline */
188*57400dccSAndrii Nakryiko __msg("returning from callee:")
189*57400dccSAndrii Nakryiko __msg("Unreleased reference id=1")
leak_iter_from_subprog_fail(void * ctx)190*57400dccSAndrii Nakryiko int leak_iter_from_subprog_fail(void *ctx)
191*57400dccSAndrii Nakryiko {
192*57400dccSAndrii Nakryiko 	subprog_with_iter();
193*57400dccSAndrii Nakryiko 
194*57400dccSAndrii Nakryiko 	return 0;
195*57400dccSAndrii Nakryiko }
196*57400dccSAndrii Nakryiko 
197*57400dccSAndrii Nakryiko SEC("?raw_tp")
198*57400dccSAndrii Nakryiko __success __log_level(2)
199*57400dccSAndrii Nakryiko __msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)")
valid_stack_reuse(void * ctx)200*57400dccSAndrii Nakryiko int valid_stack_reuse(void *ctx)
201*57400dccSAndrii Nakryiko {
202*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
203*57400dccSAndrii Nakryiko 
204*57400dccSAndrii Nakryiko 	asm volatile (
205*57400dccSAndrii Nakryiko 		/* create iterator */
206*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
207*57400dccSAndrii Nakryiko 		"r2 = 0;"
208*57400dccSAndrii Nakryiko 		"r3 = 1000;"
209*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
210*57400dccSAndrii Nakryiko 		/* destroy iterator */
211*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
212*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
213*57400dccSAndrii Nakryiko 
214*57400dccSAndrii Nakryiko 		/* now reuse same stack slots */
215*57400dccSAndrii Nakryiko 
216*57400dccSAndrii Nakryiko 		/* create iterator */
217*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
218*57400dccSAndrii Nakryiko 		"r2 = 0;"
219*57400dccSAndrii Nakryiko 		"r3 = 1000;"
220*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
221*57400dccSAndrii Nakryiko 		/* destroy iterator */
222*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
223*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
224*57400dccSAndrii Nakryiko 		:
225*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
226*57400dccSAndrii Nakryiko 		: __clobber_common
227*57400dccSAndrii Nakryiko 	);
228*57400dccSAndrii Nakryiko 
229*57400dccSAndrii Nakryiko 	return 0;
230*57400dccSAndrii Nakryiko }
231*57400dccSAndrii Nakryiko 
232*57400dccSAndrii Nakryiko SEC("?raw_tp")
233*57400dccSAndrii Nakryiko __failure __msg("expected uninitialized iter_num as arg #1")
double_create_fail(void * ctx)234*57400dccSAndrii Nakryiko int double_create_fail(void *ctx)
235*57400dccSAndrii Nakryiko {
236*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
237*57400dccSAndrii Nakryiko 
238*57400dccSAndrii Nakryiko 	asm volatile (
239*57400dccSAndrii Nakryiko 		/* create iterator */
240*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
241*57400dccSAndrii Nakryiko 		"r2 = 0;"
242*57400dccSAndrii Nakryiko 		"r3 = 1000;"
243*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
244*57400dccSAndrii Nakryiko 		/* (attempt to) create iterator again */
245*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
246*57400dccSAndrii Nakryiko 		"r2 = 0;"
247*57400dccSAndrii Nakryiko 		"r3 = 1000;"
248*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
249*57400dccSAndrii Nakryiko 		/* destroy iterator */
250*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
251*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
252*57400dccSAndrii Nakryiko 		:
253*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
254*57400dccSAndrii Nakryiko 		: __clobber_common
255*57400dccSAndrii Nakryiko 	);
256*57400dccSAndrii Nakryiko 
257*57400dccSAndrii Nakryiko 	return 0;
258*57400dccSAndrii Nakryiko }
259*57400dccSAndrii Nakryiko 
260*57400dccSAndrii Nakryiko SEC("?raw_tp")
261*57400dccSAndrii Nakryiko __failure __msg("expected an initialized iter_num as arg #1")
double_destroy_fail(void * ctx)262*57400dccSAndrii Nakryiko int double_destroy_fail(void *ctx)
263*57400dccSAndrii Nakryiko {
264*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
265*57400dccSAndrii Nakryiko 
266*57400dccSAndrii Nakryiko 	asm volatile (
267*57400dccSAndrii Nakryiko 		/* create iterator */
268*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
269*57400dccSAndrii Nakryiko 		"r2 = 0;"
270*57400dccSAndrii Nakryiko 		"r3 = 1000;"
271*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
272*57400dccSAndrii Nakryiko 		/* destroy iterator */
273*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
274*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
275*57400dccSAndrii Nakryiko 		/* (attempt to) destroy iterator again */
276*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
277*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
278*57400dccSAndrii Nakryiko 		:
279*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
280*57400dccSAndrii Nakryiko 		: __clobber_common
281*57400dccSAndrii Nakryiko 	);
282*57400dccSAndrii Nakryiko 
283*57400dccSAndrii Nakryiko 	return 0;
284*57400dccSAndrii Nakryiko }
285*57400dccSAndrii Nakryiko 
286*57400dccSAndrii Nakryiko SEC("?raw_tp")
287*57400dccSAndrii Nakryiko __failure __msg("expected an initialized iter_num as arg #1")
next_without_new_fail(void * ctx)288*57400dccSAndrii Nakryiko int next_without_new_fail(void *ctx)
289*57400dccSAndrii Nakryiko {
290*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
291*57400dccSAndrii Nakryiko 
292*57400dccSAndrii Nakryiko 	asm volatile (
293*57400dccSAndrii Nakryiko 		/* don't create iterator and try to iterate*/
294*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
295*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_next];"
296*57400dccSAndrii Nakryiko 		/* destroy iterator */
297*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
298*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
299*57400dccSAndrii Nakryiko 		:
300*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
301*57400dccSAndrii Nakryiko 		: __clobber_common
302*57400dccSAndrii Nakryiko 	);
303*57400dccSAndrii Nakryiko 
304*57400dccSAndrii Nakryiko 	return 0;
305*57400dccSAndrii Nakryiko }
306*57400dccSAndrii Nakryiko 
307*57400dccSAndrii Nakryiko SEC("?raw_tp")
308*57400dccSAndrii Nakryiko __failure __msg("expected an initialized iter_num as arg #1")
next_after_destroy_fail(void * ctx)309*57400dccSAndrii Nakryiko int next_after_destroy_fail(void *ctx)
310*57400dccSAndrii Nakryiko {
311*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
312*57400dccSAndrii Nakryiko 
313*57400dccSAndrii Nakryiko 	asm volatile (
314*57400dccSAndrii Nakryiko 		/* create iterator */
315*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
316*57400dccSAndrii Nakryiko 		"r2 = 0;"
317*57400dccSAndrii Nakryiko 		"r3 = 1000;"
318*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
319*57400dccSAndrii Nakryiko 		/* destroy iterator */
320*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
321*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
322*57400dccSAndrii Nakryiko 		/* don't create iterator and try to iterate*/
323*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
324*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_next];"
325*57400dccSAndrii Nakryiko 		:
326*57400dccSAndrii Nakryiko 		: __imm_ptr(iter), ITER_HELPERS
327*57400dccSAndrii Nakryiko 		: __clobber_common
328*57400dccSAndrii Nakryiko 	);
329*57400dccSAndrii Nakryiko 
330*57400dccSAndrii Nakryiko 	return 0;
331*57400dccSAndrii Nakryiko }
332*57400dccSAndrii Nakryiko 
333*57400dccSAndrii Nakryiko SEC("?raw_tp")
334*57400dccSAndrii Nakryiko __failure __msg("invalid read from stack")
read_from_iter_slot_fail(void)335*57400dccSAndrii Nakryiko int __naked read_from_iter_slot_fail(void)
336*57400dccSAndrii Nakryiko {
337*57400dccSAndrii Nakryiko 	asm volatile (
338*57400dccSAndrii Nakryiko 		/* r6 points to struct bpf_iter_num on the stack */
339*57400dccSAndrii Nakryiko 		"r6 = r10;"
340*57400dccSAndrii Nakryiko 		"r6 += -24;"
341*57400dccSAndrii Nakryiko 
342*57400dccSAndrii Nakryiko 		/* create iterator */
343*57400dccSAndrii Nakryiko 		"r1 = r6;"
344*57400dccSAndrii Nakryiko 		"r2 = 0;"
345*57400dccSAndrii Nakryiko 		"r3 = 1000;"
346*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
347*57400dccSAndrii Nakryiko 
348*57400dccSAndrii Nakryiko 		/* attemp to leak bpf_iter_num state */
349*57400dccSAndrii Nakryiko 		"r7 = *(u64 *)(r6 + 0);"
350*57400dccSAndrii Nakryiko 		"r8 = *(u64 *)(r6 + 8);"
351*57400dccSAndrii Nakryiko 
352*57400dccSAndrii Nakryiko 		/* destroy iterator */
353*57400dccSAndrii Nakryiko 		"r1 = r6;"
354*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_destroy];"
355*57400dccSAndrii Nakryiko 
356*57400dccSAndrii Nakryiko 		/* leak bpf_iter_num state */
357*57400dccSAndrii Nakryiko 		"r0 = r7;"
358*57400dccSAndrii Nakryiko 		"if r7 > r8 goto +1;"
359*57400dccSAndrii Nakryiko 		"r0 = r8;"
360*57400dccSAndrii Nakryiko 		"exit;"
361*57400dccSAndrii Nakryiko 		:
362*57400dccSAndrii Nakryiko 		: ITER_HELPERS
363*57400dccSAndrii Nakryiko 		: __clobber_common, "r6", "r7", "r8"
364*57400dccSAndrii Nakryiko 	);
365*57400dccSAndrii Nakryiko }
366*57400dccSAndrii Nakryiko 
367*57400dccSAndrii Nakryiko int zero;
368*57400dccSAndrii Nakryiko 
369*57400dccSAndrii Nakryiko SEC("?raw_tp")
370*57400dccSAndrii Nakryiko __failure
__flag(BPF_F_TEST_STATE_FREQ)371*57400dccSAndrii Nakryiko __flag(BPF_F_TEST_STATE_FREQ)
372*57400dccSAndrii Nakryiko __msg("Unreleased reference")
373*57400dccSAndrii Nakryiko int stacksafe_should_not_conflate_stack_spill_and_iter(void *ctx)
374*57400dccSAndrii Nakryiko {
375*57400dccSAndrii Nakryiko 	struct bpf_iter_num iter;
376*57400dccSAndrii Nakryiko 
377*57400dccSAndrii Nakryiko 	asm volatile (
378*57400dccSAndrii Nakryiko 		/* Create a fork in logic, with general setup as follows:
379*57400dccSAndrii Nakryiko 		 *   - fallthrough (first) path is valid;
380*57400dccSAndrii Nakryiko 		 *   - branch (second) path is invalid.
381*57400dccSAndrii Nakryiko 		 * Then depending on what we do in fallthrough vs branch path,
382*57400dccSAndrii Nakryiko 		 * we try to detect bugs in func_states_equal(), regsafe(),
383*57400dccSAndrii Nakryiko 		 * refsafe(), stack_safe(), and similar by tricking verifier
384*57400dccSAndrii Nakryiko 		 * into believing that branch state is a valid subset of
385*57400dccSAndrii Nakryiko 		 * a fallthrough state. Verifier should reject overall
386*57400dccSAndrii Nakryiko 		 * validation, unless there is a bug somewhere in verifier
387*57400dccSAndrii Nakryiko 		 * logic.
388*57400dccSAndrii Nakryiko 		 */
389*57400dccSAndrii Nakryiko 		"call %[bpf_get_prandom_u32];"
390*57400dccSAndrii Nakryiko 		"r6 = r0;"
391*57400dccSAndrii Nakryiko 		"call %[bpf_get_prandom_u32];"
392*57400dccSAndrii Nakryiko 		"r7 = r0;"
393*57400dccSAndrii Nakryiko 
394*57400dccSAndrii Nakryiko 		"if r6 > r7 goto bad;" /* fork */
395*57400dccSAndrii Nakryiko 
396*57400dccSAndrii Nakryiko 		/* spill r6 into stack slot of bpf_iter_num var */
397*57400dccSAndrii Nakryiko 		"*(u64 *)(%[iter] + 0) = r6;"
398*57400dccSAndrii Nakryiko 
399*57400dccSAndrii Nakryiko 		"goto skip_bad;"
400*57400dccSAndrii Nakryiko 
401*57400dccSAndrii Nakryiko 	"bad:"
402*57400dccSAndrii Nakryiko 		/* create iterator in the same stack slot */
403*57400dccSAndrii Nakryiko 		"r1 = %[iter];"
404*57400dccSAndrii Nakryiko 		"r2 = 0;"
405*57400dccSAndrii Nakryiko 		"r3 = 1000;"
406*57400dccSAndrii Nakryiko 		"call %[bpf_iter_num_new];"
407*57400dccSAndrii Nakryiko 
408*57400dccSAndrii Nakryiko 		/* but then forget about it and overwrite it back to r6 spill */
409*57400dccSAndrii Nakryiko 		"*(u64 *)(%[iter] + 0) = r6;"
410*57400dccSAndrii Nakryiko 
411*57400dccSAndrii Nakryiko 	"skip_bad:"
412*57400dccSAndrii Nakryiko 		"goto +0;" /* force checkpoint */
413*57400dccSAndrii Nakryiko 
414*57400dccSAndrii Nakryiko 		/* corrupt stack slots, if they are really dynptr */
415*57400dccSAndrii Nakryiko 		"*(u64 *)(%[iter] + 0) = r6;"
416*57400dccSAndrii Nakryiko 		:
417*57400dccSAndrii Nakryiko 		: __imm_ptr(iter),
418*57400dccSAndrii Nakryiko 		  __imm_addr(zero),
419*57400dccSAndrii Nakryiko 		  __imm(bpf_get_prandom_u32),
420*57400dccSAndrii Nakryiko 		  __imm(bpf_dynptr_from_mem),
421*57400dccSAndrii Nakryiko 		  ITER_HELPERS
422*57400dccSAndrii Nakryiko 		: __clobber_common, "r6", "r7"
423*57400dccSAndrii Nakryiko 	);
424*57400dccSAndrii Nakryiko 
425*57400dccSAndrii Nakryiko 	return 0;
426*57400dccSAndrii Nakryiko }
427