xref: /linux/tools/testing/selftests/bpf/progs/cb_refs.c (revision e6a901a00822659181c93c86d8bbc2a17779fddc)
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 
7 struct map_value {
8 	struct prog_test_ref_kfunc __kptr *ptr;
9 };
10 
11 struct {
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 
18 static __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 
26 SEC("?tc")
27 int 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 
40 static __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 
49 SEC("?tc")
50 int 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 
67 static __always_inline int cb(void *map, void *key, void *value, void *ctx)
68 {
69 	return 0;
70 }
71 
72 static __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 
85 SEC("?tc")
86 int 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 
100 SEC("?tc")
101 int 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 
114 char _license[] SEC("license") = "GPL";
115