1// SPDX-License-Identifier: GPL-2.0
2#include <vmlinux.h>
3#include <bpf/bpf_tracing.h>
4#include <bpf/bpf_helpers.h>
5#include <bpf/bpf_core_read.h>
6#include "bpf_experimental.h"
7
8#include "linked_list.h"
9
10#define INIT                                                  \
11	struct map_value *v, *v2, *iv, *iv2;                  \
12	struct foo *f, *f1, *f2;                              \
13	struct bar *b;                                        \
14	void *map;                                            \
15                                                              \
16	map = bpf_map_lookup_elem(&map_of_maps, &(int){ 0 }); \
17	if (!map)                                             \
18		return 0;                                     \
19	v = bpf_map_lookup_elem(&array_map, &(int){ 0 });     \
20	if (!v)                                               \
21		return 0;                                     \
22	v2 = bpf_map_lookup_elem(&array_map, &(int){ 0 });    \
23	if (!v2)                                              \
24		return 0;                                     \
25	iv = bpf_map_lookup_elem(map, &(int){ 0 });           \
26	if (!iv)                                              \
27		return 0;                                     \
28	iv2 = bpf_map_lookup_elem(map, &(int){ 0 });          \
29	if (!iv2)                                             \
30		return 0;                                     \
31	f = bpf_obj_new(typeof(*f));                          \
32	if (!f)                                               \
33		return 0;                                     \
34	f1 = f;                                               \
35	f2 = bpf_obj_new(typeof(*f2));                        \
36	if (!f2) {                                            \
37		bpf_obj_drop(f1);                             \
38		return 0;                                     \
39	}                                                     \
40	b = bpf_obj_new(typeof(*b));                          \
41	if (!b) {                                             \
42		bpf_obj_drop(f2);                             \
43		bpf_obj_drop(f1);                             \
44		return 0;                                     \
45	}
46
47#define CHECK(test, op, hexpr)                              \
48	SEC("?tc")                                          \
49	int test##_missing_lock_##op(void *ctx)             \
50	{                                                   \
51		INIT;                                       \
52		void (*p)(void *) = (void *)&bpf_list_##op; \
53		p(hexpr);                                   \
54		return 0;                                   \
55	}
56
57CHECK(kptr, pop_front, &f->head);
58CHECK(kptr, pop_back, &f->head);
59
60CHECK(global, pop_front, &ghead);
61CHECK(global, pop_back, &ghead);
62
63CHECK(map, pop_front, &v->head);
64CHECK(map, pop_back, &v->head);
65
66CHECK(inner_map, pop_front, &iv->head);
67CHECK(inner_map, pop_back, &iv->head);
68
69#undef CHECK
70
71#define CHECK(test, op, hexpr, nexpr)					\
72	SEC("?tc")							\
73	int test##_missing_lock_##op(void *ctx)				\
74	{								\
75		INIT;							\
76		bpf_list_##op(hexpr, nexpr);				\
77		return 0;						\
78	}
79
80CHECK(kptr, push_front, &f->head, &b->node);
81CHECK(kptr, push_back, &f->head, &b->node);
82
83CHECK(global, push_front, &ghead, &f->node2);
84CHECK(global, push_back, &ghead, &f->node2);
85
86CHECK(map, push_front, &v->head, &f->node2);
87CHECK(map, push_back, &v->head, &f->node2);
88
89CHECK(inner_map, push_front, &iv->head, &f->node2);
90CHECK(inner_map, push_back, &iv->head, &f->node2);
91
92#undef CHECK
93
94#define CHECK(test, op, lexpr, hexpr)                       \
95	SEC("?tc")                                          \
96	int test##_incorrect_lock_##op(void *ctx)           \
97	{                                                   \
98		INIT;                                       \
99		void (*p)(void *) = (void *)&bpf_list_##op; \
100		bpf_spin_lock(lexpr);                       \
101		p(hexpr);                                   \
102		return 0;                                   \
103	}
104
105#define CHECK_OP(op)                                           \
106	CHECK(kptr_kptr, op, &f1->lock, &f2->head);            \
107	CHECK(kptr_global, op, &f1->lock, &ghead);             \
108	CHECK(kptr_map, op, &f1->lock, &v->head);              \
109	CHECK(kptr_inner_map, op, &f1->lock, &iv->head);       \
110                                                               \
111	CHECK(global_global, op, &glock2, &ghead);             \
112	CHECK(global_kptr, op, &glock, &f1->head);             \
113	CHECK(global_map, op, &glock, &v->head);               \
114	CHECK(global_inner_map, op, &glock, &iv->head);        \
115                                                               \
116	CHECK(map_map, op, &v->lock, &v2->head);               \
117	CHECK(map_kptr, op, &v->lock, &f2->head);              \
118	CHECK(map_global, op, &v->lock, &ghead);               \
119	CHECK(map_inner_map, op, &v->lock, &iv->head);         \
120                                                               \
121	CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head); \
122	CHECK(inner_map_kptr, op, &iv->lock, &f2->head);       \
123	CHECK(inner_map_global, op, &iv->lock, &ghead);        \
124	CHECK(inner_map_map, op, &iv->lock, &v->head);
125
126CHECK_OP(pop_front);
127CHECK_OP(pop_back);
128
129#undef CHECK
130#undef CHECK_OP
131
132#define CHECK(test, op, lexpr, hexpr, nexpr)				\
133	SEC("?tc")							\
134	int test##_incorrect_lock_##op(void *ctx)			\
135	{								\
136		INIT;							\
137		bpf_spin_lock(lexpr);					\
138		bpf_list_##op(hexpr, nexpr);				\
139		return 0;						\
140	}
141
142#define CHECK_OP(op)							\
143	CHECK(kptr_kptr, op, &f1->lock, &f2->head, &b->node);		\
144	CHECK(kptr_global, op, &f1->lock, &ghead, &f->node2);		\
145	CHECK(kptr_map, op, &f1->lock, &v->head, &f->node2);		\
146	CHECK(kptr_inner_map, op, &f1->lock, &iv->head, &f->node2);	\
147									\
148	CHECK(global_global, op, &glock2, &ghead, &f->node2);		\
149	CHECK(global_kptr, op, &glock, &f1->head, &b->node);		\
150	CHECK(global_map, op, &glock, &v->head, &f->node2);		\
151	CHECK(global_inner_map, op, &glock, &iv->head, &f->node2);	\
152									\
153	CHECK(map_map, op, &v->lock, &v2->head, &f->node2);		\
154	CHECK(map_kptr, op, &v->lock, &f2->head, &b->node);		\
155	CHECK(map_global, op, &v->lock, &ghead, &f->node2);		\
156	CHECK(map_inner_map, op, &v->lock, &iv->head, &f->node2);	\
157									\
158	CHECK(inner_map_inner_map, op, &iv->lock, &iv2->head, &f->node2);\
159	CHECK(inner_map_kptr, op, &iv->lock, &f2->head, &b->node);	\
160	CHECK(inner_map_global, op, &iv->lock, &ghead, &f->node2);	\
161	CHECK(inner_map_map, op, &iv->lock, &v->head, &f->node2);
162
163CHECK_OP(push_front);
164CHECK_OP(push_back);
165
166#undef CHECK
167#undef CHECK_OP
168#undef INIT
169
170SEC("?kprobe/xyz")
171int map_compat_kprobe(void *ctx)
172{
173	bpf_list_push_front(&ghead, NULL);
174	return 0;
175}
176
177SEC("?kretprobe/xyz")
178int map_compat_kretprobe(void *ctx)
179{
180	bpf_list_push_front(&ghead, NULL);
181	return 0;
182}
183
184SEC("?tracepoint/xyz")
185int map_compat_tp(void *ctx)
186{
187	bpf_list_push_front(&ghead, NULL);
188	return 0;
189}
190
191SEC("?perf_event")
192int map_compat_perf(void *ctx)
193{
194	bpf_list_push_front(&ghead, NULL);
195	return 0;
196}
197
198SEC("?raw_tp/xyz")
199int map_compat_raw_tp(void *ctx)
200{
201	bpf_list_push_front(&ghead, NULL);
202	return 0;
203}
204
205SEC("?raw_tp.w/xyz")
206int map_compat_raw_tp_w(void *ctx)
207{
208	bpf_list_push_front(&ghead, NULL);
209	return 0;
210}
211
212SEC("?tc")
213int obj_type_id_oor(void *ctx)
214{
215	bpf_obj_new_impl(~0UL, NULL);
216	return 0;
217}
218
219SEC("?tc")
220int obj_new_no_composite(void *ctx)
221{
222	bpf_obj_new_impl(bpf_core_type_id_local(int), (void *)42);
223	return 0;
224}
225
226SEC("?tc")
227int obj_new_no_struct(void *ctx)
228{
229
230	bpf_obj_new(union { int data; unsigned udata; });
231	return 0;
232}
233
234SEC("?tc")
235int obj_drop_non_zero_off(void *ctx)
236{
237	void *f;
238
239	f = bpf_obj_new(struct foo);
240	if (!f)
241		return 0;
242	bpf_obj_drop(f+1);
243	return 0;
244}
245
246SEC("?tc")
247int new_null_ret(void *ctx)
248{
249	return bpf_obj_new(struct foo)->data;
250}
251
252SEC("?tc")
253int obj_new_acq(void *ctx)
254{
255	bpf_obj_new(struct foo);
256	return 0;
257}
258
259SEC("?tc")
260int use_after_drop(void *ctx)
261{
262	struct foo *f;
263
264	f = bpf_obj_new(typeof(*f));
265	if (!f)
266		return 0;
267	bpf_obj_drop(f);
268	return f->data;
269}
270
271SEC("?tc")
272int ptr_walk_scalar(void *ctx)
273{
274	struct test1 {
275		struct test2 {
276			struct test2 *next;
277		} *ptr;
278	} *p;
279
280	p = bpf_obj_new(typeof(*p));
281	if (!p)
282		return 0;
283	bpf_this_cpu_ptr(p->ptr);
284	return 0;
285}
286
287SEC("?tc")
288int direct_read_lock(void *ctx)
289{
290	struct foo *f;
291
292	f = bpf_obj_new(typeof(*f));
293	if (!f)
294		return 0;
295	return *(int *)&f->lock;
296}
297
298SEC("?tc")
299int direct_write_lock(void *ctx)
300{
301	struct foo *f;
302
303	f = bpf_obj_new(typeof(*f));
304	if (!f)
305		return 0;
306	*(int *)&f->lock = 0;
307	return 0;
308}
309
310SEC("?tc")
311int direct_read_head(void *ctx)
312{
313	struct foo *f;
314
315	f = bpf_obj_new(typeof(*f));
316	if (!f)
317		return 0;
318	return *(int *)&f->head;
319}
320
321SEC("?tc")
322int direct_write_head(void *ctx)
323{
324	struct foo *f;
325
326	f = bpf_obj_new(typeof(*f));
327	if (!f)
328		return 0;
329	*(int *)&f->head = 0;
330	return 0;
331}
332
333SEC("?tc")
334int direct_read_node(void *ctx)
335{
336	struct foo *f;
337
338	f = bpf_obj_new(typeof(*f));
339	if (!f)
340		return 0;
341	return *(int *)&f->node2;
342}
343
344SEC("?tc")
345int direct_write_node(void *ctx)
346{
347	struct foo *f;
348
349	f = bpf_obj_new(typeof(*f));
350	if (!f)
351		return 0;
352	*(int *)&f->node2 = 0;
353	return 0;
354}
355
356static __always_inline
357int use_after_unlock(bool push_front)
358{
359	struct foo *f;
360
361	f = bpf_obj_new(typeof(*f));
362	if (!f)
363		return 0;
364	bpf_spin_lock(&glock);
365	f->data = 42;
366	if (push_front)
367		bpf_list_push_front(&ghead, &f->node2);
368	else
369		bpf_list_push_back(&ghead, &f->node2);
370	bpf_spin_unlock(&glock);
371
372	return f->data;
373}
374
375SEC("?tc")
376int use_after_unlock_push_front(void *ctx)
377{
378	return use_after_unlock(true);
379}
380
381SEC("?tc")
382int use_after_unlock_push_back(void *ctx)
383{
384	return use_after_unlock(false);
385}
386
387static __always_inline
388int list_double_add(bool push_front)
389{
390	struct foo *f;
391
392	f = bpf_obj_new(typeof(*f));
393	if (!f)
394		return 0;
395	bpf_spin_lock(&glock);
396	if (push_front) {
397		bpf_list_push_front(&ghead, &f->node2);
398		bpf_list_push_front(&ghead, &f->node2);
399	} else {
400		bpf_list_push_back(&ghead, &f->node2);
401		bpf_list_push_back(&ghead, &f->node2);
402	}
403	bpf_spin_unlock(&glock);
404
405	return 0;
406}
407
408SEC("?tc")
409int double_push_front(void *ctx)
410{
411	return list_double_add(true);
412}
413
414SEC("?tc")
415int double_push_back(void *ctx)
416{
417	return list_double_add(false);
418}
419
420SEC("?tc")
421int no_node_value_type(void *ctx)
422{
423	void *p;
424
425	p = bpf_obj_new(struct { int data; });
426	if (!p)
427		return 0;
428	bpf_spin_lock(&glock);
429	bpf_list_push_front(&ghead, p);
430	bpf_spin_unlock(&glock);
431
432	return 0;
433}
434
435SEC("?tc")
436int incorrect_value_type(void *ctx)
437{
438	struct bar *b;
439
440	b = bpf_obj_new(typeof(*b));
441	if (!b)
442		return 0;
443	bpf_spin_lock(&glock);
444	bpf_list_push_front(&ghead, &b->node);
445	bpf_spin_unlock(&glock);
446
447	return 0;
448}
449
450SEC("?tc")
451int incorrect_node_var_off(struct __sk_buff *ctx)
452{
453	struct foo *f;
454
455	f = bpf_obj_new(typeof(*f));
456	if (!f)
457		return 0;
458	bpf_spin_lock(&glock);
459	bpf_list_push_front(&ghead, (void *)&f->node2 + ctx->protocol);
460	bpf_spin_unlock(&glock);
461
462	return 0;
463}
464
465SEC("?tc")
466int incorrect_node_off1(void *ctx)
467{
468	struct foo *f;
469
470	f = bpf_obj_new(typeof(*f));
471	if (!f)
472		return 0;
473	bpf_spin_lock(&glock);
474	bpf_list_push_front(&ghead, (void *)&f->node2 + 1);
475	bpf_spin_unlock(&glock);
476
477	return 0;
478}
479
480SEC("?tc")
481int incorrect_node_off2(void *ctx)
482{
483	struct foo *f;
484
485	f = bpf_obj_new(typeof(*f));
486	if (!f)
487		return 0;
488	bpf_spin_lock(&glock);
489	bpf_list_push_front(&ghead, &f->node);
490	bpf_spin_unlock(&glock);
491
492	return 0;
493}
494
495SEC("?tc")
496int no_head_type(void *ctx)
497{
498	void *p;
499
500	p = bpf_obj_new(typeof(struct { int data; }));
501	if (!p)
502		return 0;
503	bpf_spin_lock(&glock);
504	bpf_list_push_front(p, NULL);
505	bpf_spin_lock(&glock);
506
507	return 0;
508}
509
510SEC("?tc")
511int incorrect_head_var_off1(struct __sk_buff *ctx)
512{
513	struct foo *f;
514
515	f = bpf_obj_new(typeof(*f));
516	if (!f)
517		return 0;
518	bpf_spin_lock(&glock);
519	bpf_list_push_front((void *)&ghead + ctx->protocol, &f->node2);
520	bpf_spin_unlock(&glock);
521
522	return 0;
523}
524
525SEC("?tc")
526int incorrect_head_var_off2(struct __sk_buff *ctx)
527{
528	struct foo *f;
529
530	f = bpf_obj_new(typeof(*f));
531	if (!f)
532		return 0;
533	bpf_spin_lock(&glock);
534	bpf_list_push_front((void *)&f->head + ctx->protocol, &f->node2);
535	bpf_spin_unlock(&glock);
536
537	return 0;
538}
539
540SEC("?tc")
541int incorrect_head_off1(void *ctx)
542{
543	struct foo *f;
544	struct bar *b;
545
546	f = bpf_obj_new(typeof(*f));
547	if (!f)
548		return 0;
549	b = bpf_obj_new(typeof(*b));
550	if (!b) {
551		bpf_obj_drop(f);
552		return 0;
553	}
554
555	bpf_spin_lock(&f->lock);
556	bpf_list_push_front((void *)&f->head + 1, &b->node);
557	bpf_spin_unlock(&f->lock);
558
559	return 0;
560}
561
562SEC("?tc")
563int incorrect_head_off2(void *ctx)
564{
565	struct foo *f;
566
567	f = bpf_obj_new(typeof(*f));
568	if (!f)
569		return 0;
570
571	bpf_spin_lock(&glock);
572	bpf_list_push_front((void *)&ghead + 1, &f->node2);
573	bpf_spin_unlock(&glock);
574
575	return 0;
576}
577
578static __always_inline
579int pop_ptr_off(void *(*op)(void *head))
580{
581	struct {
582		struct bpf_list_head head __contains(foo, node2);
583		struct bpf_spin_lock lock;
584	} *p;
585	struct bpf_list_node *n;
586
587	p = bpf_obj_new(typeof(*p));
588	if (!p)
589		return 0;
590	bpf_spin_lock(&p->lock);
591	n = op(&p->head);
592	bpf_spin_unlock(&p->lock);
593
594	if (!n)
595		return 0;
596	bpf_spin_lock((void *)n);
597	return 0;
598}
599
600SEC("?tc")
601int pop_front_off(void *ctx)
602{
603	return pop_ptr_off((void *)bpf_list_pop_front);
604}
605
606SEC("?tc")
607int pop_back_off(void *ctx)
608{
609	return pop_ptr_off((void *)bpf_list_pop_back);
610}
611
612char _license[] SEC("license") = "GPL";
613