Confirm that the following sleepable prog states fail verification: * bpf_rcu_read_unlock before bpf_spin_unlock * RCU CS will last at least as long as spin_lock CS Also confirm that correct usage passes verification, specifically: * Explicit use of bpf_rcu_read_{lock, unlock} in sleepable test prog * Implied RCU CS due to spin_lock CS None of the selftest progs actually attach to bpf_testmod's bpf_testmod_test_read.
Unified · Split
| @@ -8,6 +8,9 @@ · 7 unmodified lines | ||
| 8 | 8 | #include "bpf_misc.h" |
| 9 | 9 | #include "bpf_experimental.h" |
| 10 | 10 | |
| 11 | +extern void bpf_rcu_read_lock(void) __ksym; | |
| 12 | +extern void bpf_rcu_read_unlock(void) __ksym; | |
| 13 | + | |
| 11 | 14 | struct node_data { |
| 12 | 15 | long key; |
| 13 | 16 | long list_data; |
| @@ -497,4 +500,72 @@ long rbtree_wrong_owner_remove_fail_a2(void *ctx) · 483 unmodified lines | ||
| 497 | 500 | return 0; |
| 498 | 501 | } |
| 499 | 502 | |
| 503 | +SEC("?fentry.s/bpf_testmod_test_read") | |
| 504 | +__success | |
| 505 | +int BPF_PROG(rbtree_sleepable_rcu, | |
| 506 | + struct file *file, struct kobject *kobj, | |
| 507 | + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len) | |
| 508 | +{ | |
| 509 | + struct bpf_rb_node *rb; | |
| 510 | + struct node_data *n, *m = NULL; | |
| 511 | + | |
| 512 | + n = bpf_obj_new(typeof(*n)); | |
| 513 | + if (!n) | |
| 514 | + return 0; | |
| 515 | + | |
| 516 | + bpf_rcu_read_lock(); | |
| 517 | + bpf_spin_lock(&lock); | |
| 518 | + bpf_rbtree_add(&root, &n->r, less); | |
| 519 | + rb = bpf_rbtree_first(&root); | |
| 520 | + if (!rb) | |
| 521 | + goto err_out; | |
| 522 | + | |
| 523 | + rb = bpf_rbtree_remove(&root, rb); | |
| 524 | + if (!rb) | |
| 525 | + goto err_out; | |
| 526 | + | |
| 527 | + m = container_of(rb, struct node_data, r); | |
| 528 | + | |
| 529 | +err_out: | |
| 530 | + bpf_spin_unlock(&lock); | |
| 531 | + bpf_rcu_read_unlock(); | |
| 532 | + if (m) | |
| 533 | + bpf_obj_drop(m); | |
| 534 | + return 0; | |
| 535 | +} | |
| 536 | + | |
| 537 | +SEC("?fentry.s/bpf_testmod_test_read") | |
| 538 | +__success | |
| 539 | +int BPF_PROG(rbtree_sleepable_rcu_no_explicit_rcu_lock, | |
| 540 | + struct file *file, struct kobject *kobj, | |
| 541 | + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len) | |
| 542 | +{ | |
| 543 | + struct bpf_rb_node *rb; | |
| 544 | + struct node_data *n, *m = NULL; | |
| 545 | + | |
| 546 | + n = bpf_obj_new(typeof(*n)); | |
| 547 | + if (!n) | |
| 548 | + return 0; | |
| 549 | + | |
| 550 | + /* No explicit bpf_rcu_read_lock */ | |
| 551 | + bpf_spin_lock(&lock); | |
| 552 | + bpf_rbtree_add(&root, &n->r, less); | |
| 553 | + rb = bpf_rbtree_first(&root); | |
| 554 | + if (!rb) | |
| 555 | + goto err_out; | |
| 556 | + | |
| 557 | + rb = bpf_rbtree_remove(&root, rb); | |
| 558 | + if (!rb) | |
| 559 | + goto err_out; | |
| 560 | + | |
| 561 | + m = container_of(rb, struct node_data, r); | |
| 562 | + | |
| 563 | +err_out: | |
| 564 | + bpf_spin_unlock(&lock); | |
| 565 | + /* No explicit bpf_rcu_read_unlock */ | |
| 566 | + if (m) | |
| 567 | + bpf_obj_drop(m); | |
| 568 | + return 0; | |
| 569 | +} | |
| 570 | + | |
| 500 | 571 | char _license[] SEC("license") = "GPL"; |
| @@ -13,6 +13,9 @@ struct node_acquire { · 12 unmodified lines | ||
| 13 | 13 | struct bpf_refcount refcount; |
| 14 | 14 | }; |
| 15 | 15 | |
| 16 | +extern void bpf_rcu_read_lock(void) __ksym; | |
| 17 | +extern void bpf_rcu_read_unlock(void) __ksym; | |
| 18 | + | |
| 16 | 19 | #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) |
| 17 | 20 | private(A) struct bpf_spin_lock glock; |
| 18 | 21 | private(A) struct bpf_rb_root groot __contains(node_acquire, node); |
| @@ -71,4 +74,29 @@ long rbtree_refcounted_node_ref_escapes_owning_input(void *ctx) · 52 unmodified lines | ||
| 71 | 74 | return 0; |
| 72 | 75 | } |
| 73 | 76 | |
| 77 | +SEC("?fentry.s/bpf_testmod_test_read") | |
| 78 | +__failure __msg("function calls are not allowed while holding a lock") | |
| 79 | +int BPF_PROG(rbtree_fail_sleepable_lock_across_rcu, | |
| 80 | + struct file *file, struct kobject *kobj, | |
| 81 | + struct bin_attribute *bin_attr, char *buf, loff_t off, size_t len) | |
| 82 | +{ | |
| 83 | + struct node_acquire *n; | |
| 84 | + | |
| 85 | + n = bpf_obj_new(typeof(*n)); | |
| 86 | + if (!n) | |
| 87 | + return 0; | |
| 88 | + | |
| 89 | + /* spin_{lock,unlock} are in different RCU CS */ | |
| 90 | + bpf_rcu_read_lock(); | |
| 91 | + bpf_spin_lock(&glock); | |
| 92 | + bpf_rbtree_add(&groot, &n->node, less); | |
| 93 | + bpf_rcu_read_unlock(); | |
| 94 | + | |
| 95 | + bpf_rcu_read_lock(); | |
| 96 | + bpf_spin_unlock(&glock); | |
| 97 | + bpf_rcu_read_unlock(); | |
| 98 | + | |
| 99 | + return 0; | |
| 100 | +} | |
| 101 | + | |
| 74 | 102 | char _license[] SEC("license") = "GPL"; |