Netdev List
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: edumazet@google.com
Cc: davem@davemloft.net, kuba@kernel.org, pabeni@redhat.com,
	horms@kernel.org, andrew+netdev@lunn.ch, idosch@nvidia.com,
	kuniyu@google.com, netdev@vger.kernel.org,
	eric.dumazet@gmail.com
Subject: Re: [PATCH v2 net-next 2/8] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()
Date: Tue, 08 Sep 2026 18:03:53 +0000	[thread overview]
Message-ID: <178889063307.219967.14381012776534349065@kernel.org> (raw)
In-Reply-To: <20260906180111.1973188-3-edumazet@google.com>

Thank you for your contribution! Sashiko AI review found 2 potential
issue(s) to consider:

Critical: 0 · High: 0 · Medium: 2 · Low: 0 (2)

- [Medium] The commit message's first justification is factually wrong
  for this tree: vxlan_vnifilter_dump_dev() does NOT run "without RTNL".
- [Medium] The paginated RTM_GETTUNNEL dump resumes with pure ordinal
  cursors: cb->args[0] is the position of the vxlan device in the netdev…

Pre-existing issues:
- [Low] Claim: vxlan_vnifilter_dump_dev() reads vg->num_vnis
  (drivers/net/vxlan/vxlan_vnifilter.c:355) with a plain, unannotated…
- [Low] Claim: the dump reads v->remote_ip (a multi-word union
  vxlan_addr) via vxlan_addr_equal() in vxlan_vnifilter_dump_dev()…

--

vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev()

The patch converts the vg->vni_list walk in vxlan_vnifilter_dump_dev() from
list_for_each_entry_safe() to list_for_each_entry_rcu(), and clears
cb->args[1] on the two early returns so a stale VNI offset is not carried
over to the next device in a paginated RTM_GETTUNNEL dump.

A few questions below.

> 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.

[Severity: Medium]
Is the "without RTNL" part of this description accurate for this tree?

RTM_GETTUNNEL is registered in vxlan_vnifilter_rtnl_msg_handlers[] with a
flags value of 0, i.e. without RTNL_FLAG_DUMP_UNLOCKED:

drivers/net/vxlan/vxlan_vnifilter.c:
static const struct rtnl_msg_handler vxlan_vnifilter_rtnl_msg_handlers[] = {
	{THIS_MODULE, PF_BRIDGE, RTM_GETTUNNEL, NULL, vxlan_vnifilter_dump, 0},

rtnetlink_dump_start() then routes the dump through rtnl_dumpit(), which
holds RTNL for the whole callback:

net/core/rtnetlink.c:rtnl_dumpit() {
	const bool needs_lock = !(cb->flags & RTNL_FLAG_DUMP_UNLOCKED);
	...
	if (needs_lock)
		rtnl_lock();
	err = dumpit(skb, cb);
	if (needs_lock)
		rtnl_unlock();
}

The writers of vg->vni_list (__vxlan_vni_add_list() and
__vxlan_vni_del_list(), reached from the RTM_NEWTUNNEL/RTM_DELTUNNEL doit
handlers, which are also registered with flags 0, and from
vxlan_vnigroup_uninit()) run under RTNL as well.

So the previous list_for_each_entry_safe() walk appears to already have
been serialized against those writers, which would make item 1 RCU hygiene
or preparation for a future unlocked dump rather than a fix for a live
race. Item 2 does look like a real fix, since RTNL is dropped between dump
pages. Could the changelog be adjusted so that the Fixes: tag is not read
as covering an RCU list-traversal race that cannot happen today?

> diff --git a/drivers/net/vxlan/vxlan_vnifilter.c b/drivers/net/vxlan/vxlan_vnifilter.c
> index dd94085e08865..fdf25d01bc1fb 100644
> --- a/drivers/net/vxlan/vxlan_vnifilter.c
> +++ b/drivers/net/vxlan/vxlan_vnifilter.c

[ ... ]

> @@ -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;
> +	}

[Severity: Low]
This isn't a bug introduced by this patch, but a note on the plain read of
vg->num_vnis in vxlan_vnifilter_dump_dev() while this early return is being
touched.

The counter is updated non-atomically after the RCU list publish/unlink:

drivers/net/vxlan/vxlan_vnifilter.c:__vxlan_vni_add_list() {
	list_add_rcu(&v->vlist, hpos);
	vg->num_vnis++;
}

drivers/net/vxlan/vxlan_vnifilter.c:__vxlan_vni_del_list() {
	list_del_rcu(&v->vlist);
	vg->num_vnis--;
}

As shown above, both the dump and these writers currently run under RTNL,
so there is no concurrency and no torn read today. Should this dump ever be
converted to RTNL_FLAG_DUMP_UNLOCKED, would this read need READ_ONCE(), or
could the num_vnis test simply be dropped now that the traversal itself is
RCU-safe?

>  
>  	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;

[Severity: Medium]
Can the same "silently skipping its first N VNIs" symptom still be reached
through device and VNI list churn between dump pages, so that clearing
cb->args[1] on the early returns is not sufficient?

Both cursors are pure ordinals. In vxlan_vnifilter_dump() the device is
matched by position:

	for_each_netdev_rcu(net, dev) {
		if (!netif_is_vxlan(dev))
			continue;
		if (idx < s_idx)
			goto skip;
		err = vxlan_vnifilter_dump_dev(dev, skb, cb);

and in vxlan_vnifilter_dump_dev() the VNI is matched by position:

	list_for_each_entry_rcu(v, &vg->vni_list, vlist) {
		if (idx < s_idx) {
			idx++;
			continue;
		}
	...
	cb->args[1] = err ? idx : 0;

RTNL is released at the end of each rtnl_dumpit() invocation, so with vxlan
devices [D0, D1, D2] and a page ending mid-D1 (cb->args[0] = 1,
cb->args[1] = 40):

  - D0 is deleted between recvmsg() rounds
  - enumeration becomes [D1, D2]
  - position 1 is now D2, so D1's remaining VNIs are never emitted
  - vxlan_vnifilter_dump_dev(D2) is entered with s_idx = 40 and drops D2's
    first 40 VNI entries

The new cb->args[1] = 0 statements do not help here because dump_dev() is
never called for the device the offset belonged to. bridge vni add/del
between pages shifts the VNI ordinals in the same way.

Additionally, unlike other rtnetlink dumps over the same netdev list, no
generation counter is recorded and NLM_F_DUMP_INTR is never set, so the
skipping or duplication is invisible to user space:

net/core/rtnetlink.c:rtnl_dump_ifinfo() {
	cb->seq = tgt_net->dev_base_seq;
	nl_dump_check_consistent(cb, nlmsg_hdr(skb));
}

Would keying the VNI offset to the device ifindex rather than its list
position, plus a cb->seq generation with nl_dump_check_consistent(), be the
more robust fix?

[Severity: Low]
This is a pre-existing issue and not introduced by this patch, but while the
traversal is being made RCU-aware: the loop body reads the multi-word
v->remote_ip union without any annotation, both here

		if (!dump_stats && vnirange(vend, v) == 1 &&
		    vxlan_addr_equal(&v->remote_ip, &vend->remote_ip)) {

and in vxlan_fill_vni_filter_entry(), while the writer replaces it in place:

drivers/net/vxlan/vxlan_vnifilter.c:vxlan_vni_update_group() {
	if (group)
		memcpy(&vninode->remote_ip, group, sizeof(vninode->remote_ip));
}

Since both paths hold RTNL as shown above, a torn value cannot be observed
by this dump today. If the dump becomes unlocked, would the in-place memcpy
of remote_ip need to be replaced by something RCU-safe so that a mismatched
sa_family and address pair cannot be emitted or used for range coalescing?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260906180111.1973188-1-edumazet%40google.com

  reply	other threads:[~2026-09-08 18:03 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-06 18:01 [PATCH v2 net-next 0/8] vxlan: convert configuration to RCU and drop RTNL in vxlan_fill_info() Eric Dumazet
2026-09-06 18:01 ` [PATCH v2 net-next 1/8] vxlan: initialize _md in vxlan_xmit_one() Eric Dumazet
2026-09-06 18:01 ` [PATCH v2 net-next 2/8] vxlan: vnifilter: use list_for_each_entry_rcu() in vxlan_vnifilter_dump_dev() Eric Dumazet
2026-09-08 18:03   ` netdev-bot+sashiko [this message]
2026-09-06 18:01 ` [PATCH v2 net-next 3/8] vxlan: pass vxlan_config pointer to helper functions Eric Dumazet
2026-09-06 18:01 ` [PATCH v2 net-next 4/8] vxlan: move VXLAN_F_MDB to struct vxlan_dev flags Eric Dumazet
2026-09-06 18:01 ` [PATCH v2 net-next 5/8] vxlan: dynamically allocate struct vxlan_config Eric Dumazet
2026-09-08 18:03   ` netdev-bot+sashiko
2026-09-10  1:35     ` Jakub Kicinski
2026-09-11  2:15       ` Eric Dumazet
2026-09-06 18:01 ` [PATCH v2 net-next 6/8] vxlan: convert configuration to RCU protection Eric Dumazet
2026-09-08 18:03   ` netdev-bot+sashiko
2026-09-06 18:01 ` [PATCH v2 net-next 7/8] vxlan: remove default_dst and use vxlan_config and lowerdev Eric Dumazet
2026-09-08 18:03   ` netdev-bot+sashiko
2026-09-06 18:01 ` [PATCH v2 net-next 8/8] vxlan: no longer rely on RTNL in vxlan_fill_info() Eric Dumazet
2026-09-08 18:03   ` netdev-bot+sashiko
2026-09-10  1:40 ` [PATCH v2 net-next 0/8] vxlan: convert configuration to RCU and drop " patchwork-bot+netdevbpf

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=178889063307.219967.14381012776534349065@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=eric.dumazet@gmail.com \
    --cc=horms@kernel.org \
    --cc=idosch@nvidia.com \
    --cc=kuba@kernel.org \
    --cc=kuniyu@google.com \
    --cc=netdev@vger.kernel.org \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox