xref: /linux/tools/testing/selftests/bpf/progs/refcounted_kptr_fail.c (revision a10ea943356b9d70c5616a0a06f6fa97cfdaccb1)
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 extern void bpf_rcu_read_lock(void) __ksym;
23 extern void bpf_rcu_read_unlock(void) __ksym;
24 
25 #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8)))
26 private(A) struct bpf_spin_lock glock;
27 private(A) struct bpf_rb_root groot __contains(node_acquire, node);
28 private(B) struct bpf_spin_lock lock;
29 private(B) struct bpf_list_head head __contains(node_refcounted, list);
30 
31 static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b)
32 {
33 	struct node_acquire *node_a;
34 	struct node_acquire *node_b;
35 
36 	node_a = container_of(a, struct node_acquire, node);
37 	node_b = container_of(b, struct node_acquire, node);
38 
39 	return node_a->key < node_b->key;
40 }
41 
42 SEC("?tc")
43 __failure __msg("Unreleased reference id=4 alloc_insn={{[0-9]+}}")
44 long rbtree_refcounted_node_ref_escapes(void *ctx)
45 {
46 	struct node_acquire *n, *m;
47 
48 	n = bpf_obj_new(typeof(*n));
49 	if (!n)
50 		return 1;
51 
52 	bpf_spin_lock(&glock);
53 	bpf_rbtree_add(&groot, &n->node, less);
54 	/* m becomes an owning ref but is never drop'd or added to a tree */
55 	m = bpf_refcount_acquire(n);
56 	bpf_spin_unlock(&glock);
57 	if (!m)
58 		return 2;
59 
60 	m->key = 2;
61 	return 0;
62 }
63 
64 SEC("?tc")
65 __failure __msg("Possibly NULL pointer passed to trusted R1")
66 long refcount_acquire_maybe_null(void *ctx)
67 {
68 	struct node_acquire *n, *m;
69 
70 	n = bpf_obj_new(typeof(*n));
71 	/* Intentionally not testing !n
72 	 * it's MAYBE_NULL for refcount_acquire
73 	 */
74 	m = bpf_refcount_acquire(n);
75 	if (m)
76 		bpf_obj_drop(m);
77 	if (n)
78 		bpf_obj_drop(n);
79 
80 	return 0;
81 }
82 
83 SEC("?tc")
84 __failure __msg("Unreleased reference id=3 alloc_insn={{[0-9]+}}")
85 long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx)
86 {
87 	struct node_acquire *n, *m;
88 
89 	n = bpf_obj_new(typeof(*n));
90 	if (!n)
91 		return 1;
92 
93 	/* m becomes an owning ref but is never drop'd or added to a tree */
94 	m = bpf_refcount_acquire(n);
95 	m->key = 2;
96 
97 	bpf_spin_lock(&glock);
98 	bpf_rbtree_add(&groot, &n->node, less);
99 	bpf_spin_unlock(&glock);
100 
101 	return 0;
102 }
103 
104 SEC("?tc")
105 __failure __msg("dereference of modified ptr_ ptr R1")
106 long refcount_acquire_list_node_offset(void *ctx)
107 {
108 	struct node_refcounted *node, *base, *ref;
109 	struct bpf_list_node *list_node;
110 
111 	node = bpf_obj_new(typeof(*node));
112 	if (!node)
113 		return 1;
114 
115 	bpf_spin_lock(&lock);
116 	bpf_list_push_front(&head, &node->list);
117 	list_node = bpf_list_pop_front(&head);
118 	bpf_spin_unlock(&lock);
119 	if (!list_node)
120 		return 2;
121 
122 	base = container_of(list_node, struct node_refcounted, list);
123 	ref = bpf_refcount_acquire(list_node);
124 	if (ref)
125 		bpf_obj_drop(ref);
126 	bpf_obj_drop(base);
127 	return 0;
128 }
129 
130 SEC("?fentry.s/" SYS_PREFIX "sys_getpgid")
131 __failure __msg("function calls are not allowed while holding a lock")
132 int BPF_PROG(rbtree_fail_sleepable_lock_across_rcu,
133 	     struct file *file, struct kobject *kobj,
134 	     struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len)
135 {
136 	struct node_acquire *n;
137 
138 	n = bpf_obj_new(typeof(*n));
139 	if (!n)
140 		return 0;
141 
142 	/* spin_{lock,unlock} are in different RCU CS */
143 	bpf_rcu_read_lock();
144 	bpf_spin_lock(&glock);
145 	bpf_rbtree_add(&groot, &n->node, less);
146 	bpf_rcu_read_unlock();
147 
148 	bpf_rcu_read_lock();
149 	bpf_spin_unlock(&glock);
150 	bpf_rcu_read_unlock();
151 
152 	return 0;
153 }
154 
155 char _license[] SEC("license") = "GPL";
156