Netdev List
 help / color / mirror / Atom feed
From: netdev-bot+sashiko@kernel.org
To: kuniyu@google.com
Cc: dsahern@kernel.org, idosch@nvidia.com, andrew+netdev@lunn.ch,
	davem@davemloft.net, edumazet@kernel.org, kuba@kernel.org,
	pabeni@redhat.com, horms@kernel.org, carges@cloudflare.com,
	kuni1840@gmail.com, netdev@vger.kernel.org
Subject: Re: [PATCH v3 net-next 5/6] ipv4: Batch rt_flush_dev() in netdev_run_todo().
Date: Sun, 04 Oct 2026 23:02:22 +0000	[thread overview]
Message-ID: <179115494261.434549.1774667236869169411@kernel.org> (raw)
In-Reply-To: <20261001204752.2572265-6-kuniyu@google.com>

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

Critical: 0 · High: 1 · Medium: 0 · Low: 0

If you believe none of the issues are legitimate, please reply to each
issue of Medium severity or higher and explain why the AI is wrong.
If any of them are legitimate and you plan to respin, please let patchwork
know by including "pw-bot: cr" as a separate line at the end of your reply
(one such reply per series is enough).

- [High] Missing RCU grace period between the device-pointer swap in
  rt_flush_dev(NULL) and freeing the net_device.

--

ipv4: Batch rt_flush_dev() in netdev_run_todo().

This patch takes the IPv4 uncached route flush out of the per-device
unregister path. netdev_run_todo() now calls rt_flush_dev(NULL) once per
batch, without RTNL. With a NULL dev, rt_flush_dev() moves every uncached
route whose device has reached NETREG_UNREGISTERED over to
blackhole_netdev.

Closes: https://lore.kernel.org/netdev/20260917-hash-bucket-route-lists-v3-0-30493a37b6eb@cloudflare.com/

> diff --git a/net/core/dev.c b/net/core/dev.c
> index 0f87fd8c68923..4658e4e16d9dd 100644
> --- a/net/core/dev.c
> +++ b/net/core/dev.c

[ ... ]

> @@ -11871,6 +11872,9 @@ void netdev_run_todo(void)
>  		linkwatch_sync_dev(dev);
>  	}
>  
> +	if (!list_empty(&list))
> +		rt_flush_dev(NULL);
> +

[Severity: High]

Is there still an RCU grace period between the rt->dst.dev swap and
freeing the old net_device?

Before this patch, fib_netdev_event()->rt_flush_dev(dev) did the swap
during NETDEV_UNREGISTER. That was before the second synchronize_net() in
unregister_netdevice_many_notify(), and before the rcu_barrier() at the
top of netdev_run_todo(). So any RCU reader that had already loaded the
old rt->dst.dev was done before the device could be freed.

With this patch, the swap and the reference drop both happen after those
calls:

net/ipv4/route.c:rt_flush_dev() {
    ...
	rcu_assign_pointer(rt->dst.dev_rcu, blackhole_netdev);
	netdev_ref_replace(rt_dev, blackhole_netdev,
			   &rt->dst.dev_tracker, GFP_ATOMIC);
    ...
}

If that drops the last extra reference, netdev_wait_allrefs_any() returns
from its first check, before any rcu_barrier() or msleep():

	list_for_each_entry(dev, list, todo_list)
		if (netdev_refcnt_read(dev) == 1)
			return dev;

netdev_run_todo() then goes straight on to priv_destructor, free_netdev()
and kobject_put(). Some drivers instead call free_netdev() right after
unregister_netdev() returns. Either way, netdev_release() frees the
device without waiting:

	/* no need to wait for rcu grace period:
	 * device is dead and about to be freed.
	 */
	kfree(rcu_access_pointer(dev->ifalias));
	kvfree(dev);

The only synchronize_net() left on this path is in netdev_napi_exit(). It
runs only when dev->napi_list is not empty, so most virtual devices skip
it.

Meanwhile, a sender whose socket has an uncached IPv4 route to this device
in sk_dst_cache can be in sk_dst_check()->ipv4_dst_check()->rt_is_expired():

	rcu_read_lock();
	res = rth->rt_genid != rt_genid_ipv4(dev_net_rcu(rth->dst.dev));
	rcu_read_unlock();

rt_flush_dev() does not mark the dst obsolete, so this check still runs.
If the reader loads the old rth->dst.dev and is then preempted (under
PREEMPT_RCU) or delayed while unregistration finishes, can it read
dev->nd_net from freed memory? Other readers that use dst_dev_rcu() rely
on RCU to keep the device alive in the same way.

Deleting the device needs only CAP_NET_ADMIN in a user namespace's netns
(for example "ip link del"), so an unprivileged user with userns can
reach this path.

The later patch "ipv6: Batch rt6_uncached_list_flush_dev() in
netdev_run_todo()" seems to follow the same pattern for IPv6. At the end
of the series, netdev_run_todo() still calls rt_flush_dev(NULL) and
rt6_uncached_list_flush_dev(NULL) and goes directly into the free loop.

Would a synchronize_net() after the flush calls in netdev_run_todo() be
needed here, or is there something else that guarantees a grace period
before the device is freed?

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

  reply	other threads:[~2026-10-04 23:02 UTC|newest]

Thread overview: 17+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-01 20:47 [PATCH v3 net-next 0/6] ip: Batch flushing uncached routes per batched device unregistration Kuniyuki Iwashima
2026-10-01 20:47 ` [PATCH v3 net-next 1/6] net: Order blackhole_netdev_init(), inet_init(), and inet6_init() Kuniyuki Iwashima
2026-10-05 12:18   ` Fernando Fernandez Mancera
2026-10-01 20:47 ` [PATCH v3 net-next 2/6] ipv4: Inline inet_blackhole_dev_init() to devinet_init() Kuniyuki Iwashima
2026-10-04 23:02   ` netdev-bot+sashiko
2026-10-01 20:47 ` [PATCH v3 net-next 3/6] net: Rename dev_isalive() to netif_is_alive() Kuniyuki Iwashima
2026-10-01 20:47 ` [PATCH v3 net-next 4/6] xfrm: Check netif_is_alive() in xfrm_bundle_create() and xfrm_create_dummy_bundle() Kuniyuki Iwashima
2026-10-01 20:47 ` [PATCH v3 net-next 5/6] ipv4: Batch rt_flush_dev() in netdev_run_todo() Kuniyuki Iwashima
2026-10-04 23:02   ` netdev-bot+sashiko [this message]
2026-10-05  0:51     ` Kuniyuki Iwashima
2026-10-01 20:47 ` [PATCH v3 net-next 6/6] ipv6: Batch rt6_uncached_list_flush_dev() " Kuniyuki Iwashima
2026-10-04 23:02   ` netdev-bot+sashiko
2026-10-05  0:49     ` Kuniyuki Iwashima
2026-10-06  8:12 ` [PATCH v3 net-next 0/6] ip: Batch flushing uncached routes per batched device unregistration Ido Schimmel
2026-10-06 17:45   ` Kuniyuki Iwashima
2026-10-06 20:07 ` Chris Arges
2026-10-06 23:10 ` 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=179115494261.434549.1774667236869169411@kernel.org \
    --to=netdev-bot+sashiko@kernel.org \
    --cc=andrew+netdev@lunn.ch \
    --cc=carges@cloudflare.com \
    --cc=davem@davemloft.net \
    --cc=dsahern@kernel.org \
    --cc=edumazet@kernel.org \
    --cc=horms@kernel.org \
    --cc=idosch@nvidia.com \
    --cc=kuba@kernel.org \
    --cc=kuni1840@gmail.com \
    --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