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

source code of linux/tools/testing/selftests/bpf/progs/iters_state_safety.c