xref: /linux/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c (revision 14c5eb685cdefbd32e73d2723071ecbd8effbce9)
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 #include "bpf_misc.h"
8 
9 struct node_acquire {
10 	long key;
11 	long data;
12 	struct bpf_rb_node node;
13 	struct bpf_refcount refcount;
14 };
15 
16 struct node_refcounted {
17 	long key;
18 	struct bpf_list_node list;
19 	struct bpf_refcount refcount;
20 };
21 
22 struct node_refcount_only {
23 	long key;
24 	struct bpf_refcount refcount;
25 };
26 
27 struct map_value_refcount_only {
28 	struct node_refcount_only __kptr *node;
29 };
30 
31 struct rcu_graph_node {
32 	struct bpf_rb_node node;
33 	long data;
34 };
35 
36 struct rcu_graph_node *just_here_because_btf_bug;
37 
38 struct map_value_rcu_graph {
39 	struct rcu_graph_node __kptr *node;
40 };
41 
42 extern void bpf_rcu_read_lock(void) __ksym;
43 extern void bpf_rcu_read_unlock(void) __ksym;
44 
45 #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8)))
46 private(A) struct bpf_spin_lock glock;
47 private(A) struct bpf_rb_root groot __contains(node_acquire, node);
48 private(B) struct bpf_spin_lock lock;
49 private(B) struct bpf_list_head head __contains(node_refcounted, list);
50 private(C) struct bpf_spin_lock graph_lock;
51 private(C) struct bpf_rb_root graph_root __contains(rcu_graph_node, node);
52 
53 struct {
54 	__uint(type, BPF_MAP_TYPE_ARRAY);
55 	__type(key, int);
56 	__type(value, struct map_value_refcount_only);
57 	__uint(max_entries, 1);
58 } stashed_refcount_only SEC(".maps");
59 
60 struct {
61 	__uint(type, BPF_MAP_TYPE_ARRAY);
62 	__type(key, int);
63 	__type(value, struct map_value_rcu_graph);
64 	__uint(max_entries, 1);
65 } stashed_rcu_graph SEC(".maps");
66 
67 static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
68 {
69 	struct node_acquire *node_a;
70 	struct node_acquire *node_b;
71 
72 	node_a = container_of(a, struct node_acquire, node);
73 	node_b = container_of(b, struct node_acquire, node);
74 
75 	return node_a->key < node_b->key;
76 }
77 
78 SEC("?tc")
79 __failure __msg("Unreleased reference id=4 alloc_insn={{[0-9]+}}")
80 long rbtree_refcounted_node_ref_escapes(void *ctx)
81 {
82 	struct node_acquire *n, *m;
83 
84 	n = bpf_obj_new(typeof(*n));
85 	if (!n)
86 		return 1;
87 
88 	bpf_spin_lock(&glock);
89 	bpf_rbtree_add(&groot, &n->node, less);
90 	/* m becomes an owning ref but is never drop'd or added to a tree */
91 	m = bpf_refcount_acquire(n);
92 	bpf_spin_unlock(&glock);
93 	if (!m)
94 		return 2;
95 
96 	m->key = 2;
97 	return 0;
98 }
99 
100 SEC("?tc")
101 __failure __msg("Possibly NULL pointer passed to trusted R1")
102 __msg("requires a non-NULL value of type (void *)")
103 long refcount_acquire_maybe_null(void *ctx)
104 {
105 	struct node_acquire *n, *m;
106 
107 	n = bpf_obj_new(typeof(*n));
108 	/* Intentionally not testing !n
109 	 * it's MAYBE_NULL for refcount_acquire
110 	 */
111 	m = bpf_refcount_acquire(n);
112 	if (m)
113 		bpf_obj_drop(m);
114 	if (n)
115 		bpf_obj_drop(n);
116 
117 	return 0;
118 }
119 
120 SEC("?tc")
121 __failure __msg("R1 is neither owning or non-owning ref")
122 __msg("expects a pointer to a BPF-managed refcounted object, but R1 is a context pointer")
123 long refcount_acquire_non_object(void *ctx)
124 {
125 	return bpf_refcount_acquire(ctx) != NULL;
126 }
127 
128 SEC("?syscall")
129 __failure __msg("Possibly NULL pointer passed to trusted R1")
130 long refcount_acquire_rcu_map_kptr_unchecked_drop(void *ctx)
131 {
132 	struct map_value_refcount_only *mapval;
133 	struct node_refcount_only *tmp, *n, *m;
134 	int idx = 0;
135 
136 	/* Force Clang to emit complete BTF for struct node_refcount_only. */
137 	tmp = bpf_obj_new(typeof(*tmp));
138 	if (!tmp)
139 		return 3;
140 	bpf_obj_drop(tmp);
141 
142 	mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx);
143 	if (!mapval)
144 		return 1;
145 
146 	bpf_rcu_read_lock();
147 	n = mapval->node;
148 	if (!n) {
149 		bpf_rcu_read_unlock();
150 		return 2;
151 	}
152 	m = bpf_refcount_acquire(n);
153 	bpf_rcu_read_unlock();
154 
155 	bpf_obj_drop(m);
156 
157 	return 0;
158 }
159 
160 SEC("?syscall")
161 __failure
162 __msg("bpf_rbtree_remove can only take non-owning or refcounted "
163       "bpf_rb_node pointer")
164 long rbtree_remove_after_rcu_unlock(void *ctx)
165 {
166 	struct map_value_rcu_graph *mapval;
167 	struct bpf_rb_node *rb_node;
168 	struct rcu_graph_node *node;
169 	int idx = 0;
170 
171 	mapval = bpf_map_lookup_elem(&stashed_rcu_graph, &idx);
172 	if (!mapval)
173 		return 0;
174 
175 	bpf_rcu_read_lock();
176 	node = mapval->node;
177 	if (!node) {
178 		bpf_rcu_read_unlock();
179 		return 0;
180 	}
181 	bpf_rcu_read_unlock();
182 
183 	bpf_spin_lock(&graph_lock);
184 	rb_node = bpf_rbtree_remove(&graph_root, &node->node);
185 	bpf_spin_unlock(&graph_lock);
186 	if (rb_node)
187 		bpf_obj_drop(container_of(rb_node, struct rcu_graph_node, node));
188 
189 	return 0;
190 }
191 
192 SEC("?syscall")
193 __failure __msg("R1 is neither owning or non-owning ref")
194 long refcount_acquire_after_rcu_unlock(void *ctx)
195 {
196 	struct map_value_refcount_only *mapval;
197 	struct node_refcount_only *node, *ref;
198 	int idx = 0;
199 
200 	mapval = bpf_map_lookup_elem(&stashed_refcount_only, &idx);
201 	if (!mapval)
202 		return 0;
203 
204 	bpf_rcu_read_lock();
205 	node = mapval->node;
206 	if (!node) {
207 		bpf_rcu_read_unlock();
208 		return 0;
209 	}
210 	bpf_rcu_read_unlock();
211 
212 	ref = bpf_refcount_acquire(node);
213 	if (ref)
214 		bpf_obj_drop(ref);
215 
216 	return 0;
217 }
218 
219 SEC("?syscall")
220 __failure __msg("invalid mem access 'scalar'")
221 long graph_kptr_after_spin_unlock(void *ctx)
222 {
223 	struct map_value_rcu_graph *mapval;
224 	struct rcu_graph_node *node;
225 	int idx = 0;
226 
227 	mapval = bpf_map_lookup_elem(&stashed_rcu_graph, &idx);
228 	if (!mapval)
229 		return 0;
230 
231 	bpf_spin_lock(&graph_lock);
232 	node = mapval->node;
233 	if (!node) {
234 		bpf_spin_unlock(&graph_lock);
235 		return 0;
236 	}
237 	bpf_spin_unlock(&graph_lock);
238 
239 	return node->data;
240 }
241 
242 SEC("?tc")
243 __failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}")
244 long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
245 {
246 	struct node_acquire *n, *m;
247 
248 	n = bpf_obj_new(typeof(*n));
249 	if (!n)
250 		return 1;
251 
252 	/* m becomes an owning ref but is never drop'd or added to a tree */
253 	m = bpf_refcount_acquire(n);
254 	m->key = 2;
255 
256 	bpf_spin_lock(&glock);
257 	bpf_rbtree_add(&groot, &n->node, less);
258 	bpf_spin_unlock(&glock);
259 
260 	return 0;
261 }
262 
263 SEC("?tc")
264 __failure __msg("dereference of modified ptr_ ptr R1")
265 long refcount_acquire_list_node_offset(void *ctx)
266 {
267 	struct node_refcounted *node, *base, *ref;
268 	struct bpf_list_node *list_node;
269 
270 	node = bpf_obj_new(typeof(*node));
271 	if (!node)
272 		return 1;
273 
274 	bpf_spin_lock(&lock);
275 	bpf_list_push_front(&head, &node->list);
276 	list_node = bpf_list_pop_front(&head);
277 	bpf_spin_unlock(&lock);
278 	if (!list_node)
279 		return 2;
280 
281 	base = container_of(list_node, struct node_refcounted, list);
282 	ref = bpf_refcount_acquire(list_node);
283 	if (ref)
284 		bpf_obj_drop(ref);
285 	bpf_obj_drop(base);
286 	return 0;
287 }
288 
289 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
290 __failure __msg("function calls are not allowed while holding a lock")
291 int BPF_PROG(rbtree_fail_sleepable_lock_across_rcu,
292 	     struct file *file, struct kobject *kobj,
293 	     struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len)
294 {
295 	struct node_acquire *n;
296 
297 	n = bpf_obj_new(typeof(*n));
298 	if (!n)
299 		return 0;
300 
301 	/* spin_{lock,unlock} are in different RCU CS */
302 	bpf_rcu_read_lock();
303 	bpf_spin_lock(&glock);
304 	bpf_rbtree_add(&groot, &n->node, less);
305 	bpf_rcu_read_unlock();
306 
307 	bpf_rcu_read_lock();
308 	bpf_spin_unlock(&glock);
309 	bpf_rcu_read_unlock();
310 
311 	return 0;
312 }
313 
314 char _license[] SEC("license") = "GPL";
315