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
10 char _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
17 SEC("?raw_tp")
18 __success
force_clang_to_emit_btf_for_externs(void * ctx)19 int 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
31 SEC("?raw_tp")
32 __success __log_level(2)
33 __msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)")
create_and_destroy(void * ctx)34 int 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
55 SEC("?raw_tp")
56 __failure __msg("Unreleased reference id=1")
create_and_forget_to_destroy_fail(void * ctx)57 int 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
75 SEC("?raw_tp")
76 __failure __msg("expected an initialized iter_num as arg #1")
destroy_without_creating_fail(void * ctx)77 int 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
93 SEC("?raw_tp")
94 __failure __msg("expected an initialized iter_num as arg #1")
compromise_iter_w_direct_write_fail(void * ctx)95 int 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
120 SEC("?raw_tp")
121 __failure __msg("Unreleased reference id=1")
compromise_iter_w_direct_write_and_skip_destroy_fail(void * ctx)122 int 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
145 SEC("?raw_tp")
146 __failure __msg("expected an initialized iter_num as arg #1")
compromise_iter_w_helper_write_fail(void * ctx)147 int 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
subprog_with_iter(void)176 static __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
185 SEC("?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")
leak_iter_from_subprog_fail(void * ctx)190 int leak_iter_from_subprog_fail(void *ctx)
191 {
192 subprog_with_iter();
193
194 return 0;
195 }
196
197 SEC("?raw_tp")
198 __success __log_level(2)
199 __msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)")
valid_stack_reuse(void * ctx)200 int 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
232 SEC("?raw_tp")
233 __failure __msg("expected uninitialized iter_num as arg #1")
double_create_fail(void * ctx)234 int 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
260 SEC("?raw_tp")
261 __failure __msg("expected an initialized iter_num as arg #1")
double_destroy_fail(void * ctx)262 int 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
286 SEC("?raw_tp")
287 __failure __msg("expected an initialized iter_num as arg #1")
next_without_new_fail(void * ctx)288 int 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
307 SEC("?raw_tp")
308 __failure __msg("expected an initialized iter_num as arg #1")
next_after_destroy_fail(void * ctx)309 int 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
333 SEC("?raw_tp")
334 __failure __msg("invalid read from stack")
read_from_iter_slot_fail(void)335 int __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
367 int zero;
368
369 SEC("?raw_tp")
370 __failure
__flag(BPF_F_TEST_STATE_FREQ)371 __flag(BPF_F_TEST_STATE_FREQ)
372 __msg("Unreleased reference")
373 int 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