vxlan_vnifilter_dump_dev() runs under rcu_read_lock() without RTNL when dumping VNI filter entries via RTM_GETTUNNEL. 1) Currently it traverses vg->vni_list using list_for_each_entry_safe(), which performs raw pointer accesses without RCU dereference barriers. Since concurrent RTNL writers modify vg->vni_list using list_add_rcu() and list_del_rcu(), use list_for_each_entry_rcu() instead. 2) If vxlan_vnifilter_dump_dev() returns early because VXLAN_F_VNIFILTER is not set or vg has no VNIs, cb->args[1] was not cleared. If a paginated dump was in progress, this leaked a non-zero cb->args[1] to the next device in vxlan_vnifilter_dump(), silently skipping its first N VNIs. Clear cb->args[1] on early returns. Fixes: f9c4bb0b245c ("vxlan: vni filtering support on collect metadata device") Signed-off-by: Eric Dumazet --- drivers/net/vxlan/vxlan_vnifilter.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c index ddfa24ad16f9303d7796e4a199b16dd5cc62047c..53213542fa3ecf67d66ba6bf41f7ac51cf8fb471 100644 --- a/drivers/net/vxlan/vxlan_vnifilter.c +++ b/drivers/net/vxlan/vxlan_vnifilter.c @@ -333,7 +333,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, struct sk_buff *skb, struct netlink_callback *cb) { - struct vxlan_vni_node *tmp, *v, *vbegin = NULL, *vend = NULL; + struct vxlan_vni_node *v, *vbegin = NULL, *vend = NULL; struct vxlan_dev *vxlan = netdev_priv(dev); struct tunnel_msg *new_tmsg, *tmsg; int idx = 0, s_idx = cb->args[1]; @@ -342,13 +342,17 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, bool dump_stats; int err = 0; - if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) + if (!(vxlan->cfg.flags & VXLAN_F_VNIFILTER)) { + cb->args[1] = 0; return -EINVAL; + } /* RCU needed because of the vni locking rules (rcu || rtnl) */ vg = rcu_dereference(vxlan->vnigrp); - if (!vg || !vg->num_vnis) + if (!vg || !vg->num_vnis) { + cb->args[1] = 0; return 0; + } tmsg = nlmsg_data(cb->nlh); dump_stats = !!(tmsg->flags & TUNNEL_MSG_FLAG_STATS); @@ -362,7 +366,7 @@ static int vxlan_vnifilter_dump_dev(const struct net_device *dev, new_tmsg->family = PF_BRIDGE; new_tmsg->ifindex = dev->ifindex; - list_for_each_entry_safe(v, tmp, &vg->vni_list, vlist) { + list_for_each_entry_rcu(v, &vg->vni_list, vlist) { if (idx < s_idx) { idx++; continue; -- 2.55.0.970.g62bdec98f9-goog