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
less(struct bpf_rb_node * a,const struct bpf_rb_node * b)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]+}}")
rbtree_refcounted_node_ref_escapes(void * ctx)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 *)")
refcount_acquire_maybe_null(void * ctx)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")
refcount_acquire_non_object(void * ctx)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")
refcount_acquire_rcu_map_kptr_unchecked_drop(void * ctx)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")
rbtree_remove_after_rcu_unlock(void * ctx)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")
refcount_acquire_after_rcu_unlock(void * ctx)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'")
graph_kptr_after_spin_unlock(void * ctx)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]+}}")
rbtree_refcounted_node_ref_escapes_owning_input(void * ctx)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")
refcount_acquire_list_node_offset(void * ctx)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")
BPF_PROG(rbtree_fail_sleepable_lock_across_rcu,struct file * file,struct kobject * kobj,struct bin_attribute * bin_attr,char * buf,loff_t off,size_t len)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