1 // SPDX-License-Identifier: GPL-2.0
2 #include <vmlinux.h>
3 #include <bpf/bpf_tracing.h>
4 #include <bpf/bpf_helpers.h>
5 
6 struct map_value {
7 	struct prog_test_ref_kfunc __kptr *ptr;
8 };
9 
10 struct {
11 	__uint(type, BPF_MAP_TYPE_ARRAY);
12 	__type(key, int);
13 	__type(value, struct map_value);
14 	__uint(max_entries, 16);
15 } array_map SEC(".maps");
16 
17 extern struct prog_test_ref_kfunc *bpf_kfunc_call_test_acquire(unsigned long *sp) __ksym;
18 extern void bpf_kfunc_call_test_release(struct prog_test_ref_kfunc *p) __ksym;
19 
20 static __noinline int cb1(void *map, void *key, void *value, void *ctx)
21 {
22 	void *p = *(void **)ctx;
23 	bpf_kfunc_call_test_release(p);
24 	/* Without the fix this would cause underflow */
25 	return 0;
26 }
27 
28 SEC("?tc")
29 int underflow_prog(void *ctx)
30 {
31 	struct prog_test_ref_kfunc *p;
32 	unsigned long sl = 0;
33 
34 	p = bpf_kfunc_call_test_acquire(&sl);
35 	if (!p)
36 		return 0;
37 	bpf_for_each_map_elem(&array_map, cb1, &p, 0);
38 	return 0;
39 }
40 
41 static __always_inline int cb2(void *map, void *key, void *value, void *ctx)
42 {
43 	unsigned long sl = 0;
44 
45 	*(void **)ctx = bpf_kfunc_call_test_acquire(&sl);
46 	/* Without the fix this would leak memory */
47 	return 0;
48 }
49 
50 SEC("?tc")
51 int leak_prog(void *ctx)
52 {
53 	struct prog_test_ref_kfunc *p;
54 	struct map_value *v;
55 
56 	v = bpf_map_lookup_elem(&array_map, &(int){0});
57 	if (!v)
58 		return 0;
59 
60 	p = NULL;
61 	bpf_for_each_map_elem(&array_map, cb2, &p, 0);
62 	p = bpf_kptr_xchg(&v->ptr, p);
63 	if (p)
64 		bpf_kfunc_call_test_release(p);
65 	return 0;
66 }
67 
68 static __always_inline int cb(void *map, void *key, void *value, void *ctx)
69 {
70 	return 0;
71 }
72 
73 static __always_inline int cb3(void *map, void *key, void *value, void *ctx)
74 {
75 	unsigned long sl = 0;
76 	void *p;
77 
78 	bpf_kfunc_call_test_acquire(&sl);
79 	bpf_for_each_map_elem(&array_map, cb, &p, 0);
80 	/* It should only complain here, not in cb. This is why we need
81 	 * callback_ref to be set to frameno.
82 	 */
83 	return 0;
84 }
85 
86 SEC("?tc")
87 int nested_cb(void *ctx)
88 {
89 	struct prog_test_ref_kfunc *p;
90 	unsigned long sl = 0;
91 	int sp = 0;
92 
93 	p = bpf_kfunc_call_test_acquire(&sl);
94 	if (!p)
95 		return 0;
96 	bpf_for_each_map_elem(&array_map, cb3, &sp, 0);
97 	bpf_kfunc_call_test_release(p);
98 	return 0;
99 }
100 
101 SEC("?tc")
102 int non_cb_transfer_ref(void *ctx)
103 {
104 	struct prog_test_ref_kfunc *p;
105 	unsigned long sl = 0;
106 
107 	p = bpf_kfunc_call_test_acquire(&sl);
108 	if (!p)
109 		return 0;
110 	cb1(NULL, NULL, NULL, &p);
111 	bpf_kfunc_call_test_acquire(&sl);
112 	return 0;
113 }
114 
115 char _license[] SEC("license") = "GPL";
116