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
9struct node_acquire {
10	long key;
11	long data;
12	struct bpf_rb_node node;
13	struct bpf_refcount refcount;
14};
15
16extern void bpf_rcu_read_lock(void) __ksym;
17extern void bpf_rcu_read_unlock(void) __ksym;
18
19#define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8)))
20private(A) struct bpf_spin_lock glock;
21private(A) struct bpf_rb_root groot __contains(node_acquire, node);
22
23static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
24{
25	struct node_acquire *node_a;
26	struct node_acquire *node_b;
27
28	node_a = container_of(a, struct node_acquire, node);
29	node_b = container_of(b, struct node_acquire, node);
30
31	return node_a->key < node_b->key;
32}
33
34SEC("?tc")
35__failure __msg("Unreleased reference id=4 alloc_insn=21")
36long rbtree_refcounted_node_ref_escapes(void *ctx)
37{
38	struct node_acquire *n, *m;
39
40	n = bpf_obj_new(typeof(*n));
41	if (!n)
42		return 1;
43
44	bpf_spin_lock(&glock);
45	bpf_rbtree_add(&groot, &n->node, less);
46	/* m becomes an owning ref but is never drop'd or added to a tree */
47	m = bpf_refcount_acquire(n);
48	bpf_spin_unlock(&glock);
49	if (!m)
50		return 2;
51
52	m->key = 2;
53	return 0;
54}
55
56SEC("?tc")
57__failure __msg("Possibly NULL pointer passed to trusted arg0")
58long refcount_acquire_maybe_null(void *ctx)
59{
60	struct node_acquire *n, *m;
61
62	n = bpf_obj_new(typeof(*n));
63	/* Intentionally not testing !n
64	 * it's MAYBE_NULL for refcount_acquire
65	 */
66	m = bpf_refcount_acquire(n);
67	if (m)
68		bpf_obj_drop(m);
69	if (n)
70		bpf_obj_drop(n);
71
72	return 0;
73}
74
75SEC("?tc")
76__failure __msg("Unreleased reference id=3 alloc_insn=9")
77long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
78{
79	struct node_acquire *n, *m;
80
81	n = bpf_obj_new(typeof(*n));
82	if (!n)
83		return 1;
84
85	/* m becomes an owning ref but is never drop'd or added to a tree */
86	m = bpf_refcount_acquire(n);
87	m->key = 2;
88
89	bpf_spin_lock(&glock);
90	bpf_rbtree_add(&groot, &n->node, less);
91	bpf_spin_unlock(&glock);
92
93	return 0;
94}
95
96SEC("?fentry.s/bpf_testmod_test_read")
97__failure __msg("function calls are not allowed while holding a lock")
98int BPF_PROG(rbtree_fail_sleepable_lock_across_rcu,
99	     struct file *file, struct kobject *kobj,
100	     struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len)
101{
102	struct node_acquire *n;
103
104	n = bpf_obj_new(typeof(*n));
105	if (!n)
106		return 0;
107
108	/* spin_{lock,unlock} are in different RCU CS */
109	bpf_rcu_read_lock();
110	bpf_spin_lock(&glock);
111	bpf_rbtree_add(&groot, &n->node, less);
112	bpf_rcu_read_unlock();
113
114	bpf_rcu_read_lock();
115	bpf_spin_unlock(&glock);
116	bpf_rcu_read_unlock();
117
118	return 0;
119}
120
121char _license[] SEC("license") = "GPL";
122