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