The DEVMAP_HASH branch in dev_map_redirect_multi() uses hlist_for_each_entry_safe() to iterate hash buckets, but this function runs under RCU protection (called from xdp_do_generic_redirect_map() in softirq context). Concurrent writers (__dev_map_hash_update_elem, dev_map_hash_delete_elem) modify the list using RCU primitives (hlist_add_head_rcu, hlist_del_rcu). hlist_for_each_entry_safe() performs plain pointer dereferences without rcu_dereference(), missing the acquire barrier needed to pair with writers' rcu_assign_pointer(). On weakly-ordered architectures (ARM64, POWER), a reader can observe a partially-constructed node. It also defeats CONFIG_PROVE_RCU lockdep validation and KCSAN data-race detection. Replace with hlist_for_each_entry_rcu() using rcu_read_lock_bh_held() as the lockdep condition, consistent with the rcu_dereference_check() used in the DEVMAP (non-hash) branch of the same functions. Also fix the same incorrect lockdep_is_held(&dtab->index_lock) condition in dev_map_enqueue_multi(), where the lock is not held either.
Showing the changes to kernel (1 of 1 file). Show all
Unified · Split
| @@ -665,7 +665,7 @@ int dev_map_enqueue_multi(struct xdp_frame *xdpf, struct net_device *dev_rx, · 664 unmodified lines | |||
| 665 | for (i = 0; i < dtab->n_buckets; i++) { | 665 | for (i = 0; i < dtab->n_buckets; i++) { |
| 666 | head = dev_map_index_hash(dtab, i); | 666 | head = dev_map_index_hash(dtab, i); |
| 667 | hlist_for_each_entry_rcu(dst, head, index_hlist, | 667 | hlist_for_each_entry_rcu(dst, head, index_hlist, |
| 668 | lockdep_is_held(&dtab->index_lock)) { | 668 | rcu_read_lock_bh_held()) { |
| 669 | if (!is_valid_dst(dst, xdpf)) | 669 | if (!is_valid_dst(dst, xdpf)) |
| 670 | continue; | 670 | continue; |
| 671 | 671 | ||
| @@ -747,7 +747,6 @@ int dev_map_redirect_multi(struct net_device *dev, struct sk_buff *skb, · 75 unmodified lines | |||
| 747 | struct bpf_dtab_netdev *dst, *last_dst = NULL; | 747 | struct bpf_dtab_netdev *dst, *last_dst = NULL; |
| 748 | int excluded_devices[1+MAX_NEST_DEV]; | 748 | int excluded_devices[1+MAX_NEST_DEV]; |
| 749 | struct hlist_head *head; | 749 | struct hlist_head *head; |
| 750 | struct hlist_node *next; | ||
| 751 | int num_excluded = 0; | 750 | int num_excluded = 0; |
| 752 | unsigned int i; | 751 | unsigned int i; |
| 753 | int err; | 752 | int err; |
| @@ -787,7 +786,7 @@ int dev_map_redirect_multi(struct net_device *dev, struct sk_buff *skb, · 33 unmodified lines | |||
| 787 | } else { /* BPF_MAP_TYPE_DEVMAP_HASH */ | 786 | } else { /* BPF_MAP_TYPE_DEVMAP_HASH */ |
| 788 | for (i = 0; i < dtab->n_buckets; i++) { | 787 | for (i = 0; i < dtab->n_buckets; i++) { |
| 789 | head = dev_map_index_hash(dtab, i); | 788 | head = dev_map_index_hash(dtab, i); |
| 790 | hlist_for_each_entry_safe(dst, next, head, index_hlist) { | 789 | hlist_for_each_entry_rcu(dst, head, index_hlist, rcu_read_lock_bh_held()) { |
| 791 | if (is_ifindex_excluded(excluded_devices, num_excluded, | 790 | if (is_ifindex_excluded(excluded_devices, num_excluded, |
| 792 | dst->dev->ifindex)) | 791 | dst->dev->ifindex)) |
| 793 | continue; | 792 | continue; |