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
8struct val {
9	int cnt;
10	struct bpf_spin_lock l;
11};
12
13struct {
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
20SEC("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;						\
35l0_%=:	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
53SEC("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;						\
68l0_%=:	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
86SEC("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;						\
102l0_%=:	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
120SEC("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;						\
136l0_%=:	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
154SEC("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;						\
169l0_%=:	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
188SEC("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;						\
203l0_%=:	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];			\
212l1_%=:	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
222SEC("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;						\
237l0_%=:	r6 = r0;					\
238	r1 = r0;					\
239	r1 += 4;					\
240	if r1 != 0 goto l1_%=;				\
241	call %[bpf_spin_lock];				\
242l1_%=:	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
256SEC("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;						\
271l0_%=:	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
292SEC("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;						\
307l0_%=:	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;						\
314l1_%=:	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
331SEC("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;						\
346l0_%=:	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
362static __naked __noinline __attribute__((used))
363void 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
374SEC("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;						\
389l0_%=:	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
407SEC("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;						\
425l0_%=:	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;						\
432l1_%=:	r8 = r0;					\
433	r1 = r7;					\
434	r1 += 4;					\
435	call %[bpf_spin_lock];				\
436	if r6 == 0 goto l2_%=;				\
437	goto l3_%=;					\
438l2_%=:	r7 = r8;					\
439l3_%=:	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
470SEC("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_%=;					\
513l2_%=:	/* spin_lock(r9) */				\
514	r1 = r9;					\
515	r1 += 4;					\
516	call %[bpf_spin_lock];				\
517l3_%=:	/* spin_unlock(r9) */				\
518	r1 = r9;					\
519	r1 += 4;					\
520	call %[bpf_spin_unlock];			\
521l0_%=:	/* exit(0) */					\
522	r0 = 0;						\
523l1_%=:	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
533char _license[] SEC("license") = "GPL";
534