~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/tools/testing/selftests/bpf/progs/iters_state_safety.c

Version: ~ [ linux-6.11.5 ] ~ [ linux-6.10.14 ] ~ [ linux-6.9.12 ] ~ [ linux-6.8.12 ] ~ [ linux-6.7.12 ] ~ [ linux-6.6.58 ] ~ [ linux-6.5.13 ] ~ [ linux-6.4.16 ] ~ [ linux-6.3.13 ] ~ [ linux-6.2.16 ] ~ [ linux-6.1.114 ] ~ [ linux-6.0.19 ] ~ [ linux-5.19.17 ] ~ [ linux-5.18.19 ] ~ [ linux-5.17.15 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.169 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.228 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.284 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.322 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.336 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.337 ] ~ [ linux-4.4.302 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  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
 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)")
 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")
 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")
 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")
 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")
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")
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 
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")
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)")
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")
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")
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")
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")
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")
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
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 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

sflogo.php