mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] sfc: fix use-after-free of filter_work in efx_realloc_channels()
@ 2026-10-22 13:39 Sicong Huang
  2026-10-22 13:47 ` netdev-bot+sinfo
  2026-10-22 14:36 ` Edward Cree
  0 siblings, 2 replies; 3+ messages in thread
From: Sicong Huang @ 2026-10-22 13:39 UTC (permalink / raw)
  To: netdev
  Cc: Edward Cree, Andrew Lunn, David S . Miller, Eric Dumazet,
	Jakub Kicinski, Paolo Abeni, Kees Cook, linux-net-drivers,
	linux-kernel, Sicong Huang

With CONFIG_RFS_ACCEL, each channel has a delayed work item,
channel->filter_work, that expires aRFS filters. efx_poll() queues it
with mod_delayed_work(), and efx_filter_rfs_expire() always re-arms it
with schedule_delayed_work(dwork, 30 * HZ). Once efx_poll() has queued
it, the work is therefore always pending, queued or running. It is
only cancelled in efx_remove_filters(), which is not called on the ring
resize path.

Resizing the rings via ethtool -G calls efx_realloc_channels(). It
copies each channel, swaps the copies into efx->channel[] and frees the
old channel structures. Nothing on this path cancels the old channel's
filter_work. So the old filter_work can fire after its channel has
been freed. The timer core and delayed_work_timer_fn() then access
the freed delayed_work. efx_filter_rfs_expire() gets the freed channel
back via container_of(), dereferences it and re-arms the timer inside
the freed object.

A possible race is:
CPU0                             CPU1
efx_ethtool_set_ringparam()
efx_realloc_channels()
efx_stop_all()  //NAPI stopped, timer still armed
efx_copy_channel()
swap(efx->channel[i], ...)
efx_remove_channel(old)
kfree(old)  //free here!
                                 old->filter_work.timer fires
                                 delayed_work_timer_fn()
                                 efx_filter_rfs_expire()
                                 channel = container_of(dwork, ...)
                                 channel->rfs_last_expiry  //UAF!
                                 schedule_delayed_work(dwork, 30 * HZ)

Because the work re-arms itself every 30 seconds, this is not a narrow
timing window. One successful ethtool -G after efx_poll() has queued
filter_work is enough to trigger it.

The Siena driver has its own copy of this code in
efx_siena_realloc_channels() and has the same problem.

Fix this in both drivers by calling cancel_delayed_work_sync() on
filter_work before freeing an unused channel. The work does not take
the RTNL. It only trylocks efx->rps_mutex, and the filter table locks
it takes are never held while waiting for the RTNL, so waiting for it
under the RTNL cannot deadlock. On the copy failure and rollback paths,
the freed channels are new copies whose filter_work was never queued,
so the cancel does nothing there.

Signed-off-by: Sicong Huang <congei42@163.com>
---
 drivers/net/ethernet/sfc/efx_channels.c       | 3 +++
 drivers/net/ethernet/sfc/siena/efx_channels.c | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/drivers/net/ethernet/sfc/efx_channels.c b/drivers/net/ethernet/sfc/efx_channels.c
index f4dc3f3f4416..3773a70e1e7d 100644
--- a/drivers/net/ethernet/sfc/efx_channels.c
+++ b/drivers/net/ethernet/sfc/efx_channels.c
@@ -896,6 +896,9 @@ int efx_realloc_channels(struct efx_nic *efx, u32 rxq_entries, u32 txq_entries)
 	for (i = 0; i < efx->n_channels; i++) {
 		channel = other_channel[i];
 		if (channel && channel->type->copy) {
+#ifdef CONFIG_RFS_ACCEL
+			cancel_delayed_work_sync(&channel->filter_work);
+#endif
 			efx_fini_napi_channel(channel);
 			efx_remove_channel(channel);
 			kfree(channel);
diff --git a/drivers/net/ethernet/sfc/siena/efx_channels.c b/drivers/net/ethernet/sfc/siena/efx_channels.c
index 1fc343598771..d20144f1679a 100644
--- a/drivers/net/ethernet/sfc/siena/efx_channels.c
+++ b/drivers/net/ethernet/sfc/siena/efx_channels.c
@@ -930,6 +930,9 @@ int efx_siena_realloc_channels(struct efx_nic *efx, u32 rxq_entries,
 	for (i = 0; i < efx->n_channels; i++) {
 		channel = other_channel[i];
 		if (channel && channel->type->copy) {
+#ifdef CONFIG_RFS_ACCEL
+			cancel_delayed_work_sync(&channel->filter_work);
+#endif
 			efx_fini_napi_channel(channel);
 			efx_siena_remove_channel(channel);
 			kfree(channel);

base-commit: f4377bf85887f57b59abb5479f7660a81ffe4449
-- 
2.34.1


^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-10-22 14:36 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 13:39 [PATCH net] sfc: fix use-after-free of filter_work in efx_realloc_channels() Sicong Huang
2026-10-22 13:47 ` netdev-bot+sinfo
2026-10-22 14:36 ` Edward Cree

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®