1 // SPDX-License-Identifier: GPL-2.0
2 /* Converted from tools/testing/selftests/bpf/verifier/spin_lock.c */
3
4 #include <linux/bpf.h>
5 #include <bpf/bpf_helpers.h>
6 #include "bpf_misc.h"
7
8 struct val {
9 int cnt;
10 struct bpf_spin_lock l;
11 };
12
13 struct {
14 __uint(type, BPF_MAP_TYPE_ARRAY);
15 __uint(max_entries, 1);
16 __type(key, int);
17 __type(value, struct val);
18 } map_spin_lock SEC(".maps");
19
20 SEC("cgroup/skb")
21 __description("spin_lock: test1 success")
22 __success __failure_unpriv __msg_unpriv("")
23 __retval(0)
spin_lock_test1_success(void)24 __naked void spin_lock_test1_success(void)
25 {
26 asm volatile (" \
27 r1 = 0; \
28 *(u32*)(r10 - 4) = r1; \
29 r2 = r10; \
30 r2 += -4; \
31 r1 = %[map_spin_lock] ll; \
32 call %[bpf_map_lookup_elem]; \
33 if r0 != 0 goto l0_%=; \
34 exit; \
35 l0_%=: r6 = r0; \
36 r1 = r0; \
37 r1 += 4; \
38 call %[bpf_spin_lock]; \
39 r1 = r6; \
40 r1 += 4; \
41 r0 = *(u32*)(r6 + 0); \
42 call %[bpf_spin_unlock]; \
43 r0 = 0; \
44 exit; \
45 " :
46 : __imm(bpf_map_lookup_elem),
47 __imm(bpf_spin_lock),
48 __imm(bpf_spin_unlock),
49 __imm_addr(map_spin_lock)
50 : __clobber_all);
51 }
52
53 SEC("cgroup/skb")
54 __description("spin_lock: test2 direct ld/st")
55 __failure __msg("cannot be accessed directly")
56 __failure_unpriv __msg_unpriv("")
lock_test2_direct_ld_st(void)57 __naked void lock_test2_direct_ld_st(void)
58 {
59 asm volatile (" \
60 r1 = 0; \
61 *(u32*)(r10 - 4) = r1; \
62 r2 = r10; \
63 r2 += -4; \
64 r1 = %[map_spin_lock] ll; \
65 call %[bpf_map_lookup_elem]; \
66 if r0 != 0 goto l0_%=; \
67 exit; \
68 l0_%=: r6 = r0; \
69 r1 = r0; \
70 r1 += 4; \
71 call %[bpf_spin_lock]; \
72 r1 = r6; \
73 r1 += 4; \
74 r0 = *(u32*)(r1 + 0); \
75 call %[bpf_spin_unlock]; \
76 r0 = 0; \
77 exit; \
78 " :
79 : __imm(bpf_map_lookup_elem),
80 __imm(bpf_spin_lock),
81 __imm(bpf_spin_unlock),
82 __imm_addr(map_spin_lock)
83 : __clobber_all);
84 }
85
86 SEC("cgroup/skb")
87 __description("spin_lock: test3 direct ld/st")
88 __failure __msg("cannot be accessed directly")
89 __failure_unpriv __msg_unpriv("")
__flag(BPF_F_ANY_ALIGNMENT)90 __flag(BPF_F_ANY_ALIGNMENT)
91 __naked void lock_test3_direct_ld_st(void)
92 {
93 asm volatile (" \
94 r1 = 0; \
95 *(u32*)(r10 - 4) = r1; \
96 r2 = r10; \
97 r2 += -4; \
98 r1 = %[map_spin_lock] ll; \
99 call %[bpf_map_lookup_elem]; \
100 if r0 != 0 goto l0_%=; \
101 exit; \
102 l0_%=: r6 = r0; \
103 r1 = r0; \
104 r1 += 4; \
105 call %[bpf_spin_lock]; \
106 r1 = r6; \
107 r1 += 4; \
108 r0 = *(u32*)(r6 + 1); \
109 call %[bpf_spin_unlock]; \
110 r0 = 0; \
111 exit; \
112 " :
113 : __imm(bpf_map_lookup_elem),
114 __imm(bpf_spin_lock),
115 __imm(bpf_spin_unlock),
116 __imm_addr(map_spin_lock)
117 : __clobber_all);
118 }
119
120 SEC("cgroup/skb")
121 __description("spin_lock: test4 direct ld/st")
122 __failure __msg("cannot be accessed directly")
123 __failure_unpriv __msg_unpriv("")
__flag(BPF_F_ANY_ALIGNMENT)124 __flag(BPF_F_ANY_ALIGNMENT)
125 __naked void lock_test4_direct_ld_st(void)
126 {
127 asm volatile (" \
128 r1 = 0; \
129 *(u32*)(r10 - 4) = r1; \
130 r2 = r10; \
131 r2 += -4; \
132 r1 = %[map_spin_lock] ll; \
133 call %[bpf_map_lookup_elem]; \
134 if r0 != 0 goto l0_%=; \
135 exit; \
136 l0_%=: r6 = r0; \
137 r1 = r0; \
138 r1 += 4; \
139 call %[bpf_spin_lock]; \
140 r1 = r6; \
141 r1 += 4; \
142 r0 = *(u16*)(r6 + 3); \
143 call %[bpf_spin_unlock]; \
144 r0 = 0; \
145 exit; \
146 " :
147 : __imm(bpf_map_lookup_elem),
148 __imm(bpf_spin_lock),
149 __imm(bpf_spin_unlock),
150 __imm_addr(map_spin_lock)
151 : __clobber_all);
152 }
153
154 SEC("cgroup/skb")
155 __description("spin_lock: test5 call within a locked region")
156 __failure __msg("calls are not allowed")
157 __failure_unpriv __msg_unpriv("")
call_within_a_locked_region(void)158 __naked void call_within_a_locked_region(void)
159 {
160 asm volatile (" \
161 r1 = 0; \
162 *(u32*)(r10 - 4) = r1; \
163 r2 = r10; \
164 r2 += -4; \
165 r1 = %[map_spin_lock] ll; \
166 call %[bpf_map_lookup_elem]; \
167 if r0 != 0 goto l0_%=; \
168 exit; \
169 l0_%=: r6 = r0; \
170 r1 = r0; \
171 r1 += 4; \
172 call %[bpf_spin_lock]; \
173 call %[bpf_get_prandom_u32]; \
174 r1 = r6; \
175 r1 += 4; \
176 call %[bpf_spin_unlock]; \
177 r0 = 0; \
178 exit; \
179 " :
180 : __imm(bpf_get_prandom_u32),
181 __imm(bpf_map_lookup_elem),
182 __imm(bpf_spin_lock),
183 __imm(bpf_spin_unlock),
184 __imm_addr(map_spin_lock)
185 : __clobber_all);
186 }
187
188 SEC("cgroup/skb")
189 __description("spin_lock: test6 missing unlock")
190 __failure __msg("unlock is missing")
191 __failure_unpriv __msg_unpriv("")
spin_lock_test6_missing_unlock(void)192 __naked void spin_lock_test6_missing_unlock(void)
193 {
194 asm volatile (" \
195 r1 = 0; \
196 *(u32*)(r10 - 4) = r1; \
197 r2 = r10; \
198 r2 += -4; \
199 r1 = %[map_spin_lock] ll; \
200 call %[bpf_map_lookup_elem]; \
201 if r0 != 0 goto l0_%=; \
202 exit; \
203 l0_%=: r6 = r0; \
204 r1 = r0; \
205 r1 += 4; \
206 call %[bpf_spin_lock]; \
207 r1 = r6; \
208 r1 += 4; \
209 r0 = *(u32*)(r6 + 0); \
210 if r0 != 0 goto l1_%=; \
211 call %[bpf_spin_unlock]; \
212 l1_%=: r0 = 0; \
213 exit; \
214 " :
215 : __imm(bpf_map_lookup_elem),
216 __imm(bpf_spin_lock),
217 __imm(bpf_spin_unlock),
218 __imm_addr(map_spin_lock)
219 : __clobber_all);
220 }
221
222 SEC("cgroup/skb")
223 __description("spin_lock: test7 unlock without lock")
224 __failure __msg("without taking a lock")
225 __failure_unpriv __msg_unpriv("")
lock_test7_unlock_without_lock(void)226 __naked void lock_test7_unlock_without_lock(void)
227 {
228 asm volatile (" \
229 r1 = 0; \
230 *(u32*)(r10 - 4) = r1; \
231 r2 = r10; \
232 r2 += -4; \
233 r1 = %[map_spin_lock] ll; \
234 call %[bpf_map_lookup_elem]; \
235 if r0 != 0 goto l0_%=; \
236 exit; \
237 l0_%=: r6 = r0; \
238 r1 = r0; \
239 r1 += 4; \
240 if r1 != 0 goto l1_%=; \
241 call %[bpf_spin_lock]; \
242 l1_%=: r1 = r6; \
243 r1 += 4; \
244 r0 = *(u32*)(r6 + 0); \
245 call %[bpf_spin_unlock]; \
246 r0 = 0; \
247 exit; \
248 " :
249 : __imm(bpf_map_lookup_elem),
250 __imm(bpf_spin_lock),
251 __imm(bpf_spin_unlock),
252 __imm_addr(map_spin_lock)
253 : __clobber_all);
254 }
255
256 SEC("cgroup/skb")
257 __description("spin_lock: test8 double lock")
258 __failure __msg("calls are not allowed")
259 __failure_unpriv __msg_unpriv("")
spin_lock_test8_double_lock(void)260 __naked void spin_lock_test8_double_lock(void)
261 {
262 asm volatile (" \
263 r1 = 0; \
264 *(u32*)(r10 - 4) = r1; \
265 r2 = r10; \
266 r2 += -4; \
267 r1 = %[map_spin_lock] ll; \
268 call %[bpf_map_lookup_elem]; \
269 if r0 != 0 goto l0_%=; \
270 exit; \
271 l0_%=: r6 = r0; \
272 r1 = r0; \
273 r1 += 4; \
274 call %[bpf_spin_lock]; \
275 r1 = r6; \
276 r1 += 4; \
277 call %[bpf_spin_lock]; \
278 r1 = r6; \
279 r1 += 4; \
280 r0 = *(u32*)(r6 + 0); \
281 call %[bpf_spin_unlock]; \
282 r0 = 0; \
283 exit; \
284 " :
285 : __imm(bpf_map_lookup_elem),
286 __imm(bpf_spin_lock),
287 __imm(bpf_spin_unlock),
288 __imm_addr(map_spin_lock)
289 : __clobber_all);
290 }
291
292 SEC("cgroup/skb")
293 __description("spin_lock: test9 different lock")
294 __failure __msg("unlock of different lock")
295 __failure_unpriv __msg_unpriv("")
spin_lock_test9_different_lock(void)296 __naked void spin_lock_test9_different_lock(void)
297 {
298 asm volatile (" \
299 r1 = 0; \
300 *(u32*)(r10 - 4) = r1; \
301 r2 = r10; \
302 r2 += -4; \
303 r1 = %[map_spin_lock] ll; \
304 call %[bpf_map_lookup_elem]; \
305 if r0 != 0 goto l0_%=; \
306 exit; \
307 l0_%=: r6 = r0; \
308 r2 = r10; \
309 r2 += -4; \
310 r1 = %[map_spin_lock] ll; \
311 call %[bpf_map_lookup_elem]; \
312 if r0 != 0 goto l1_%=; \
313 exit; \
314 l1_%=: r7 = r0; \
315 r1 = r6; \
316 r1 += 4; \
317 call %[bpf_spin_lock]; \
318 r1 = r7; \
319 r1 += 4; \
320 call %[bpf_spin_unlock]; \
321 r0 = 0; \
322 exit; \
323 " :
324 : __imm(bpf_map_lookup_elem),
325 __imm(bpf_spin_lock),
326 __imm(bpf_spin_unlock),
327 __imm_addr(map_spin_lock)
328 : __clobber_all);
329 }
330
331 SEC("cgroup/skb")
332 __description("spin_lock: test10 lock in subprog without unlock")
333 __failure __msg("unlock is missing")
334 __failure_unpriv __msg_unpriv("")
lock_in_subprog_without_unlock(void)335 __naked void lock_in_subprog_without_unlock(void)
336 {
337 asm volatile (" \
338 r1 = 0; \
339 *(u32*)(r10 - 4) = r1; \
340 r2 = r10; \
341 r2 += -4; \
342 r1 = %[map_spin_lock] ll; \
343 call %[bpf_map_lookup_elem]; \
344 if r0 != 0 goto l0_%=; \
345 exit; \
346 l0_%=: r6 = r0; \
347 r1 = r0; \
348 r1 += 4; \
349 call lock_in_subprog_without_unlock__1; \
350 r1 = r6; \
351 r1 += 4; \
352 call %[bpf_spin_unlock]; \
353 r0 = 1; \
354 exit; \
355 " :
356 : __imm(bpf_map_lookup_elem),
357 __imm(bpf_spin_unlock),
358 __imm_addr(map_spin_lock)
359 : __clobber_all);
360 }
361
362 static __naked __noinline __attribute__((used))
lock_in_subprog_without_unlock__1(void)363 void lock_in_subprog_without_unlock__1(void)
364 {
365 asm volatile (" \
366 call %[bpf_spin_lock]; \
367 r0 = 0; \
368 exit; \
369 " :
370 : __imm(bpf_spin_lock)
371 : __clobber_all);
372 }
373
374 SEC("tc")
375 __description("spin_lock: test11 ld_abs under lock")
376 __failure __msg("inside bpf_spin_lock")
test11_ld_abs_under_lock(void)377 __naked void test11_ld_abs_under_lock(void)
378 {
379 asm volatile (" \
380 r6 = r1; \
381 r1 = 0; \
382 *(u32*)(r10 - 4) = r1; \
383 r2 = r10; \
384 r2 += -4; \
385 r1 = %[map_spin_lock] ll; \
386 call %[bpf_map_lookup_elem]; \
387 if r0 != 0 goto l0_%=; \
388 exit; \
389 l0_%=: r7 = r0; \
390 r1 = r0; \
391 r1 += 4; \
392 call %[bpf_spin_lock]; \
393 r0 = *(u8*)skb[0]; \
394 r1 = r7; \
395 r1 += 4; \
396 call %[bpf_spin_unlock]; \
397 r0 = 0; \
398 exit; \
399 " :
400 : __imm(bpf_map_lookup_elem),
401 __imm(bpf_spin_lock),
402 __imm(bpf_spin_unlock),
403 __imm_addr(map_spin_lock)
404 : __clobber_all);
405 }
406
407 SEC("tc")
408 __description("spin_lock: regsafe compare reg->id for map value")
409 __failure __msg("bpf_spin_unlock of different lock")
__flag(BPF_F_TEST_STATE_FREQ)410 __flag(BPF_F_TEST_STATE_FREQ)
411 __naked void reg_id_for_map_value(void)
412 {
413 asm volatile (" \
414 r6 = r1; \
415 r6 = *(u32*)(r6 + %[__sk_buff_mark]); \
416 r1 = %[map_spin_lock] ll; \
417 r9 = r1; \
418 r2 = 0; \
419 *(u32*)(r10 - 4) = r2; \
420 r2 = r10; \
421 r2 += -4; \
422 call %[bpf_map_lookup_elem]; \
423 if r0 != 0 goto l0_%=; \
424 exit; \
425 l0_%=: r7 = r0; \
426 r1 = r9; \
427 r2 = r10; \
428 r2 += -4; \
429 call %[bpf_map_lookup_elem]; \
430 if r0 != 0 goto l1_%=; \
431 exit; \
432 l1_%=: r8 = r0; \
433 r1 = r7; \
434 r1 += 4; \
435 call %[bpf_spin_lock]; \
436 if r6 == 0 goto l2_%=; \
437 goto l3_%=; \
438 l2_%=: r7 = r8; \
439 l3_%=: r1 = r7; \
440 r1 += 4; \
441 call %[bpf_spin_unlock]; \
442 r0 = 0; \
443 exit; \
444 " :
445 : __imm(bpf_map_lookup_elem),
446 __imm(bpf_spin_lock),
447 __imm(bpf_spin_unlock),
448 __imm_addr(map_spin_lock),
449 __imm_const(__sk_buff_mark, offsetof(struct __sk_buff, mark))
450 : __clobber_all);
451 }
452
453 /* Make sure that regsafe() compares ids for spin lock records using
454 * check_ids():
455 * 1: r9 = map_lookup_elem(...) ; r9.id == 1
456 * 2: r8 = map_lookup_elem(...) ; r8.id == 2
457 * 3: r7 = ktime_get_ns()
458 * 4: r6 = ktime_get_ns()
459 * 5: if r6 > r7 goto <9>
460 * 6: spin_lock(r8)
461 * 7: r9 = r8
462 * 8: goto <10>
463 * 9: spin_lock(r9)
464 * 10: spin_unlock(r9) ; r9.id == 1 || r9.id == 2 and lock is active,
465 * ; second visit to (10) should be considered safe
466 * ; if check_ids() is used.
467 * 11: exit(0)
468 */
469
470 SEC("cgroup/skb")
471 __description("spin_lock: regsafe() check_ids() similar id mappings")
472 __success __msg("29: safe")
473 __failure_unpriv __msg_unpriv("")
__flag(BPF_F_TEST_STATE_FREQ)474 __log_level(2) __retval(0) __flag(BPF_F_TEST_STATE_FREQ)
475 __naked void check_ids_similar_id_mappings(void)
476 {
477 asm volatile (" \
478 r1 = 0; \
479 *(u32*)(r10 - 4) = r1; \
480 /* r9 = map_lookup_elem(...) */ \
481 r2 = r10; \
482 r2 += -4; \
483 r1 = %[map_spin_lock] ll; \
484 call %[bpf_map_lookup_elem]; \
485 if r0 == 0 goto l0_%=; \
486 r9 = r0; \
487 /* r8 = map_lookup_elem(...) */ \
488 r2 = r10; \
489 r2 += -4; \
490 r1 = %[map_spin_lock] ll; \
491 call %[bpf_map_lookup_elem]; \
492 if r0 == 0 goto l1_%=; \
493 r8 = r0; \
494 /* r7 = ktime_get_ns() */ \
495 call %[bpf_ktime_get_ns]; \
496 r7 = r0; \
497 /* r6 = ktime_get_ns() */ \
498 call %[bpf_ktime_get_ns]; \
499 r6 = r0; \
500 /* if r6 > r7 goto +5 ; no new information about the state is derived from\
501 * ; this check, thus produced verifier states differ\
502 * ; only in 'insn_idx' \
503 * spin_lock(r8) \
504 * r9 = r8 \
505 * goto unlock \
506 */ \
507 if r6 > r7 goto l2_%=; \
508 r1 = r8; \
509 r1 += 4; \
510 call %[bpf_spin_lock]; \
511 r9 = r8; \
512 goto l3_%=; \
513 l2_%=: /* spin_lock(r9) */ \
514 r1 = r9; \
515 r1 += 4; \
516 call %[bpf_spin_lock]; \
517 l3_%=: /* spin_unlock(r9) */ \
518 r1 = r9; \
519 r1 += 4; \
520 call %[bpf_spin_unlock]; \
521 l0_%=: /* exit(0) */ \
522 r0 = 0; \
523 l1_%=: exit; \
524 " :
525 : __imm(bpf_ktime_get_ns),
526 __imm(bpf_map_lookup_elem),
527 __imm(bpf_spin_lock),
528 __imm(bpf_spin_unlock),
529 __imm_addr(map_spin_lock)
530 : __clobber_all);
531 }
532
533 char _license[] SEC("license") = "GPL";
534