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_data { 10 long key; 11 long data; 12 struct bpf_rb_node node; 13 }; 14 15 #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) 16 private(A) struct bpf_spin_lock glock; 17 private(A) struct bpf_rb_root groot __contains(node_data, node); 18 private(A) struct bpf_rb_root groot2 __contains(node_data, node); 19 20 static bool less(struct bpf_rb_node *a, const struct bpf_rb_node *b) 21 { 22 struct node_data *node_a; 23 struct node_data *node_b; 24 25 node_a = container_of(a, struct node_data, node); 26 node_b = container_of(b, struct node_data, node); 27 28 return node_a->key < node_b->key; 29 } 30 31 SEC("?tc") 32 __failure __msg("bpf_spin_lock at off=16 must be held for bpf_rb_root") 33 long rbtree_api_nolock_add(void *ctx) 34 { 35 struct node_data *n; 36 37 n = bpf_obj_new(typeof(*n)); 38 if (!n) 39 return 1; 40 41 bpf_rbtree_add(&groot, &n->node, less); 42 return 0; 43 } 44 45 SEC("?tc") 46 __failure __msg("bpf_spin_lock at off=16 must be held for bpf_rb_root") 47 long rbtree_api_nolock_remove(void *ctx) 48 { 49 struct node_data *n; 50 51 n = bpf_obj_new(typeof(*n)); 52 if (!n) 53 return 1; 54 55 bpf_spin_lock(&glock); 56 bpf_rbtree_add(&groot, &n->node, less); 57 bpf_spin_unlock(&glock); 58 59 bpf_rbtree_remove(&groot, &n->node); 60 return 0; 61 } 62 63 SEC("?tc") 64 __failure __msg("bpf_spin_lock at off=16 must be held for bpf_rb_root") 65 long rbtree_api_nolock_first(void *ctx) 66 { 67 bpf_rbtree_first(&groot); 68 return 0; 69 } 70 71 SEC("?tc") 72 __failure __msg("rbtree_remove node input must be non-owning ref") 73 long rbtree_api_remove_unadded_node(void *ctx) 74 { 75 struct node_data *n, *m; 76 struct bpf_rb_node *res; 77 78 n = bpf_obj_new(typeof(*n)); 79 if (!n) 80 return 1; 81 82 m = bpf_obj_new(typeof(*m)); 83 if (!m) { 84 bpf_obj_drop(n); 85 return 1; 86 } 87 88 bpf_spin_lock(&glock); 89 bpf_rbtree_add(&groot, &n->node, less); 90 91 /* This remove should pass verifier */ 92 res = bpf_rbtree_remove(&groot, &n->node); 93 n = container_of(res, struct node_data, node); 94 95 /* This remove shouldn't, m isn't in an rbtree */ 96 res = bpf_rbtree_remove(&groot, &m->node); 97 m = container_of(res, struct node_data, node); 98 bpf_spin_unlock(&glock); 99 100 if (n) 101 bpf_obj_drop(n); 102 if (m) 103 bpf_obj_drop(m); 104 return 0; 105 } 106 107 SEC("?tc") 108 __failure __msg("Unreleased reference id=2 alloc_insn=11") 109 long rbtree_api_remove_no_drop(void *ctx) 110 { 111 struct bpf_rb_node *res; 112 struct node_data *n; 113 114 bpf_spin_lock(&glock); 115 res = bpf_rbtree_first(&groot); 116 if (!res) 117 goto unlock_err; 118 119 res = bpf_rbtree_remove(&groot, res); 120 121 n = container_of(res, struct node_data, node); 122 bpf_spin_unlock(&glock); 123 124 /* bpf_obj_drop(n) is missing here */ 125 return 0; 126 127 unlock_err: 128 bpf_spin_unlock(&glock); 129 return 1; 130 } 131 132 SEC("?tc") 133 __failure __msg("arg#1 expected pointer to allocated object") 134 long rbtree_api_add_to_multiple_trees(void *ctx) 135 { 136 struct node_data *n; 137 138 n = bpf_obj_new(typeof(*n)); 139 if (!n) 140 return 1; 141 142 bpf_spin_lock(&glock); 143 bpf_rbtree_add(&groot, &n->node, less); 144 145 /* This add should fail since n already in groot's tree */ 146 bpf_rbtree_add(&groot2, &n->node, less); 147 bpf_spin_unlock(&glock); 148 return 0; 149 } 150 151 SEC("?tc") 152 __failure __msg("rbtree_remove node input must be non-owning ref") 153 long rbtree_api_add_release_unlock_escape(void *ctx) 154 { 155 struct node_data *n; 156 157 n = bpf_obj_new(typeof(*n)); 158 if (!n) 159 return 1; 160 161 bpf_spin_lock(&glock); 162 bpf_rbtree_add(&groot, &n->node, less); 163 bpf_spin_unlock(&glock); 164 165 bpf_spin_lock(&glock); 166 /* After add() in previous critical section, n should be 167 * release_on_unlock and released after previous spin_unlock, 168 * so should not be possible to use it here 169 */ 170 bpf_rbtree_remove(&groot, &n->node); 171 bpf_spin_unlock(&glock); 172 return 0; 173 } 174 175 SEC("?tc") 176 __failure __msg("rbtree_remove node input must be non-owning ref") 177 long rbtree_api_release_aliasing(void *ctx) 178 { 179 struct node_data *n, *m, *o; 180 struct bpf_rb_node *res; 181 182 n = bpf_obj_new(typeof(*n)); 183 if (!n) 184 return 1; 185 186 bpf_spin_lock(&glock); 187 bpf_rbtree_add(&groot, &n->node, less); 188 bpf_spin_unlock(&glock); 189 190 bpf_spin_lock(&glock); 191 192 /* m and o point to the same node, 193 * but verifier doesn't know this 194 */ 195 res = bpf_rbtree_first(&groot); 196 if (!res) 197 return 1; 198 o = container_of(res, struct node_data, node); 199 200 res = bpf_rbtree_first(&groot); 201 if (!res) 202 return 1; 203 m = container_of(res, struct node_data, node); 204 205 bpf_rbtree_remove(&groot, &m->node); 206 /* This second remove shouldn't be possible. Retval of previous 207 * remove returns owning reference to m, which is the same 208 * node o's non-owning ref is pointing at 209 * 210 * In order to preserve property 211 * * owning ref must not be in rbtree 212 * * non-owning ref must be in rbtree 213 * 214 * o's ref must be invalidated after previous remove. Otherwise 215 * we'd have non-owning ref to node that isn't in rbtree, and 216 * verifier wouldn't be able to use type system to prevent remove 217 * of ref that already isn't in any tree. Would have to do runtime 218 * checks in that case. 219 */ 220 bpf_rbtree_remove(&groot, &o->node); 221 222 bpf_spin_unlock(&glock); 223 return 0; 224 } 225 226 SEC("?tc") 227 __failure __msg("rbtree_remove node input must be non-owning ref") 228 long rbtree_api_first_release_unlock_escape(void *ctx) 229 { 230 struct bpf_rb_node *res; 231 struct node_data *n; 232 233 bpf_spin_lock(&glock); 234 res = bpf_rbtree_first(&groot); 235 if (res) 236 n = container_of(res, struct node_data, node); 237 bpf_spin_unlock(&glock); 238 239 bpf_spin_lock(&glock); 240 /* After first() in previous critical section, n should be 241 * release_on_unlock and released after previous spin_unlock, 242 * so should not be possible to use it here 243 */ 244 bpf_rbtree_remove(&groot, &n->node); 245 bpf_spin_unlock(&glock); 246 return 0; 247 } 248 249 static bool less__bad_fn_call_add(struct bpf_rb_node *a, const struct bpf_rb_node *b) 250 { 251 struct node_data *node_a; 252 struct node_data *node_b; 253 254 node_a = container_of(a, struct node_data, node); 255 node_b = container_of(b, struct node_data, node); 256 bpf_rbtree_add(&groot, &node_a->node, less); 257 258 return node_a->key < node_b->key; 259 } 260 261 static bool less__bad_fn_call_remove(struct bpf_rb_node *a, const struct bpf_rb_node *b) 262 { 263 struct node_data *node_a; 264 struct node_data *node_b; 265 266 node_a = container_of(a, struct node_data, node); 267 node_b = container_of(b, struct node_data, node); 268 bpf_rbtree_remove(&groot, &node_a->node); 269 270 return node_a->key < node_b->key; 271 } 272 273 static bool less__bad_fn_call_first_unlock_after(struct bpf_rb_node *a, const struct bpf_rb_node *b) 274 { 275 struct node_data *node_a; 276 struct node_data *node_b; 277 278 node_a = container_of(a, struct node_data, node); 279 node_b = container_of(b, struct node_data, node); 280 bpf_rbtree_first(&groot); 281 bpf_spin_unlock(&glock); 282 283 return node_a->key < node_b->key; 284 } 285 286 static __always_inline 287 long add_with_cb(bool (cb)(struct bpf_rb_node *a, const struct bpf_rb_node *b)) 288 { 289 struct node_data *n; 290 291 n = bpf_obj_new(typeof(*n)); 292 if (!n) 293 return 1; 294 295 bpf_spin_lock(&glock); 296 bpf_rbtree_add(&groot, &n->node, cb); 297 bpf_spin_unlock(&glock); 298 return 0; 299 } 300 301 SEC("?tc") 302 __failure __msg("arg#1 expected pointer to allocated object") 303 long rbtree_api_add_bad_cb_bad_fn_call_add(void *ctx) 304 { 305 return add_with_cb(less__bad_fn_call_add); 306 } 307 308 SEC("?tc") 309 __failure __msg("rbtree_remove not allowed in rbtree cb") 310 long rbtree_api_add_bad_cb_bad_fn_call_remove(void *ctx) 311 { 312 return add_with_cb(less__bad_fn_call_remove); 313 } 314 315 SEC("?tc") 316 __failure __msg("can't spin_{lock,unlock} in rbtree cb") 317 long rbtree_api_add_bad_cb_bad_fn_call_first_unlock_after(void *ctx) 318 { 319 return add_with_cb(less__bad_fn_call_first_unlock_after); 320 } 321 322 char _license[] SEC("license") = "GPL"; 323