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