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) |
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("" ) |
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("" ) |
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("" ) |
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("" ) |
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("" ) |
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("" ) |
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("" ) |
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("" ) |
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 | __success |
334 | __failure_unpriv __msg_unpriv("" ) |
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)) |
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" ) |
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" ) |
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("" ) |
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 | |