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 | #include "bpf_misc.h" | 8 | #include "bpf_misc.h" |
| 9 | #include "bpf_experimental.h" | 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 | struct node_data { | 14 | struct node_data { |
| 12 | long key; | 15 | long key; |
| 13 | long list_data; | 16 | long list_data; |
| @@ -497,4 +500,72 @@ long rbtree_wrong_owner_remove_fail_a2(void *ctx) · 483 unmodified lines | |||
| 497 | return 0; | 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 | char _license[] SEC("license") = "GPL"; | 571 | char _license[] SEC("license") = "GPL"; |
| @@ -13,6 +13,9 @@ struct node_acquire { · 12 unmodified lines | |||
| 13 | struct bpf_refcount refcount; | 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 | #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) | 19 | #define private(name) SEC(".data." #name) __hidden __attribute__((aligned(8))) |
| 17 | private(A) struct bpf_spin_lock glock; | 20 | private(A) struct bpf_spin_lock glock; |
| 18 | private(A) struct bpf_rb_root groot __contains(node_acquire, node); | 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 | return 0; | 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 | char _license[] SEC("license") = "GPL"; | 102 | char _license[] SEC("license") = "GPL"; |