History

bpf: Use RCU-safe iteration in dev_map_redirect_multi() SKB path

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.

David Carlier committed 7 months ago

Commit 8ed82f81 parent 7f5b0a61 file · +2 −3Browse files

Unified · Split

kernel/bpf/devmap.c+2−3
@@ -665,7 +665,7 @@ int dev_map_enqueue_multi(struct xdp_frame *xdpf, struct net_device *dev_rx, · 664 unmodified lines
665665 for (i = 0; i < dtab->n_buckets; i++) {
666666 head = dev_map_index_hash(dtab, i);
667667 hlist_for_each_entry_rcu(dst, head, index_hlist,
668− lockdep_is_held(&dtab->index_lock)) {
668+ rcu_read_lock_bh_held()) {
669669 if (!is_valid_dst(dst, xdpf))
670670 continue;
671671
@@ -747,7 +747,6 @@ int dev_map_redirect_multi(struct net_device *dev, struct sk_buff *skb, · 75 unmodified lines
747747 struct bpf_dtab_netdev *dst, *last_dst = NULL;
748748 int excluded_devices[1+MAX_NEST_DEV];
749749 struct hlist_head *head;
750− struct hlist_node *next;
751750 int num_excluded = 0;
752751 unsigned int i;
753752 int err;
@@ -787,7 +786,7 @@ int dev_map_redirect_multi(struct net_device *dev, struct sk_buff *skb, · 33 unmodified lines
787786 } else { /* BPF_MAP_TYPE_DEVMAP_HASH */
788787 for (i = 0; i < dtab->n_buckets; i++) {
789788 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()) {
791790 if (is_ifindex_excluded(excluded_devices, num_excluded,
792791 dst->dev->ifindex))
793792 continue;