mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] net: bcmgenet: stop Tx NAPI before disabling the queues
@ 2026-09-21 12:44 Nicolai Buchwitz
  2026-09-22 12:47 ` netdev-bot+sashiko
  0 siblings, 1 reply; 3+ messages in thread
From: Nicolai Buchwitz @ 2026-09-21 12:44 UTC (permalink / raw)
  To: Doug Berger, Florian Fainelli,
	Broadcom internal kernel review list, Andrew Lunn,
	David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
  Cc: Justin Chen, Nicolai Buchwitz, netdev, linux-kernel

bcmgenet_netif_stop() disables the Tx queues first and stops Tx NAPI
several steps later. A completion in flight calls netif_tx_wake_queue() in
between, so a transmit can reach the rings after bcmgenet_fini_dma() has
freed them.

Close is safe because dev_deactivate_many() stops the qdisc first.
bcmgenet_suspend() does not, so stop Tx NAPI before the queues.

KASAN on a Raspberry Pi CM4, driven from an MTU change because suspend
freezes user space before the callback runs:

  BUG: KASAN: use-after-free in bcmgenet_xmit+0x17f8/0x2258
  Write of size 8 at addr ffffff8055844a68 by task ksoftirqd/0/14
   bcmgenet_xmit+0x17f8/0x2258
   dev_hard_start_xmit+0x13c/0x588
   sch_direct_xmit+0x108/0x340
   __dev_queue_xmit+0x1190/0x3848

Fixes: 254f3239dd07 ("net: bcmgenet: revise suspend/resume")
Signed-off-by: Nicolai Buchwitz <nb@tipi-net.de>
---
Found this, while I was preparing my jumbo frame series for genet.
The MTU change in my test comes from that series and is not in tree
yet.

 drivers/net/ethernet/broadcom/genet/bcmgenet.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
index b916080f4ff1..0789b60e664a 100644
--- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
+++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
@@ -3441,6 +3441,8 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)
 {
 	struct bcmgenet_priv *priv = netdev_priv(dev);
 
+	/* Stop completion polling before it can wake a stopped queue */
+	bcmgenet_disable_tx_napi(priv);
 	netif_tx_disable(dev);
 
 	/* Disable MAC receive */
@@ -3455,7 +3457,6 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)
 	/* Disable MAC transmit. TX DMA disabled must be done before this */
 	umac_enable_set(priv, CMD_TX_EN, false);
 
-	bcmgenet_disable_tx_napi(priv);
 	bcmgenet_disable_rx_napi(priv);
 	bcmgenet_intr_disable(priv);
 
-- 
2.53.0


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

* Re: [PATCH net] net: bcmgenet: stop Tx NAPI before disabling the queues
  2026-09-21 12:44 [PATCH net] net: bcmgenet: stop Tx NAPI before disabling the queues Nicolai Buchwitz
@ 2026-09-22 12:47 ` netdev-bot+sashiko
  2026-09-22 12:58   ` Nicolai Buchwitz
  0 siblings, 1 reply; 3+ messages in thread
From: netdev-bot+sashiko @ 2026-09-22 12:47 UTC (permalink / raw)
  To: nb
  Cc: opendmb, florian.fainelli, bcm-kernel-feedback-list,
	andrew+netdev, davem, edumazet, kuba, pabeni, justin.chen,
	netdev, linux-kernel

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] Incomplete fix: the Wake-on-LAN branch of bcmgenet_suspend()
  keeps the exact queue-stop-before-Tx-NAPI-stop ordering that this…

--

net: bcmgenet: stop Tx NAPI before disabling the queues

This moves bcmgenet_disable_tx_napi() ahead of netif_tx_disable() in
bcmgenet_netif_stop(), so an in-flight Tx completion can no longer call
netif_tx_wake_queue() after the queues have been stopped and before
bcmgenet_fini_dma() frees the rings.

> diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
> index b916080f4ff17..0789b60e664ad 100644
> --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
> +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
> @@ -3441,6 +3441,8 @@ static void bcmgenet_netif_stop(struct net_device *dev, bool stop_phy)
>  {
>  	struct bcmgenet_priv *priv = netdev_priv(dev);
>  
> +	/* Stop completion polling before it can wake a stopped queue */
> +	bcmgenet_disable_tx_napi(priv);
>  	netif_tx_disable(dev);

[Severity: High]

The commit message says:

  Close is safe because dev_deactivate_many() stops the qdisc first.
  bcmgenet_suspend() does not, so stop Tx NAPI before the queues.

bcmgenet_suspend() only reaches bcmgenet_netif_stop() in the non-WoL else
branch. Does the Wake-on-LAN branch still have the ordering this patch
declares unsafe?

drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_suspend() {
	if (device_may_wakeup(d) && priv->wolopts) {
		netif_tx_disable(dev);
		...
		/* HFB programming, interrupt clearing */
		...
		if (-ETIMEDOUT == bcmgenet_tdma_disable(priv))
			netdev_warn(priv->dev,
				    "Timed out while disabling TX DMA\n");

		bcmgenet_disable_tx_napi(priv);
		bcmgenet_disable_rx_napi(priv);
		disable_irq(priv->irq1);
		bcmgenet_tx_reclaim_all(dev);
		bcmgenet_fini_tx_napi(priv);
	} else {
		/* Teardown the interface */
		bcmgenet_netif_stop(dev, true);
	}
}

Between netif_tx_disable() and bcmgenet_disable_tx_napi() here, priv->irq1
is still enabled and the Tx NAPI is still live, and bcmgenet_tdma_disable()
spins in udelay, so the window is not short. Can bcmgenet_tx_poll() run in
that window and undo the stop?

drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_tx_poll() {
	work_done = __bcmgenet_tx_reclaim(ring->priv->dev, ring);
	if (ring->free_bds > (MAX_SKB_FRAGS + 1)) {
		txq = netdev_get_tx_queue(ring->priv->dev, ring->index);
		netif_tx_wake_queue(txq);
	}
}

Nothing later in the WoL branch re-stops the queue, and suspend does not
call dev_deactivate(), so the qdisc stays attached. sch_direct_xmit() gates
only on the per-txq stop bit, not on netif_device_present(), so the
netif_device_detach() done earlier in bcmgenet_suspend() does not block a
transmit:

net/sched/sch_generic.c:sch_direct_xmit() {
	HARD_TX_LOCK(dev, txq, smp_processor_id());
	if (!netif_xmit_frozen_or_stopped(txq))
		skb = dev_hard_start_xmit(skb, dev, txq, &ret);
}

A kernel-generated transmit is enough to hit this (ARP/ND reply, TCP
retransmit, forwarding), so freezing user space does not close it.

Two follow-on questions about that path:

Can this leak the skb and its DMA mapping? A transmit arriving after
bcmgenet_tx_reclaim_all() dma_map_single()s the skb into ring->cbs while
TDMA is already disabled, so no completion reclaims it. On the WoL-success
resume path, bcmgenet_resume() -> bcmgenet_init_tx_queues() ->
bcmgenet_init_tx_ring() resets the software and hardware indices without
walking cbs:

drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_init_tx_ring() {
	ring->cbs = priv->tx_cbs + start_ptr;
	ring->size = size;
	ring->clean_ptr = start_ptr;
	ring->c_index = 0;
	ring->free_bds = size;
	ring->write_ptr = start_ptr;
	ring->cb_ptr = start_ptr;
	ring->prod_index = 0;
}

Neither that function nor the resume path calls netdev_tx_reset_queue(), so
does the netdev_tx_sent_queue() accounting from the stray xmit leave BQL
skewed as well?

And on the WoL-failure resume path, can this reproduce the same
use-after-free quoted in the commit message?

drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_resume() {
		/* MAC was reset so complete bcmgenet_netif_stop() */
		umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, false);
		bcmgenet_rdma_disable(priv);
		bcmgenet_intr_disable(priv);
		bcmgenet_fini_dma(priv);
}

bcmgenet_fini_dma() kfree()s priv->tx_cbs while the Tx queue is still
runnable, and bcmgenet_init_dma() only reallocates it after init_umac(),
phy_init_hw() and genphy_config_aneg(), so bcmgenet_xmit() ->
bcmgenet_get_txcb() would write through ring->cbs into freed memory.

Would it make sense to reorder the WoL branch as well, moving
bcmgenet_disable_tx_napi() ahead of netif_tx_disable() there?

-- 
Sashiko AI review · https://netdev-ai.bots.linux.dev/sashiko/#/patchset/20260921124435.974581-1-nb%40tipi-net.de

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

* Re: [PATCH net] net: bcmgenet: stop Tx NAPI before disabling the queues
  2026-09-22 12:47 ` netdev-bot+sashiko
@ 2026-09-22 12:58   ` Nicolai Buchwitz
  0 siblings, 0 replies; 3+ messages in thread
From: Nicolai Buchwitz @ 2026-09-22 12:58 UTC (permalink / raw)
  To: netdev-bot+sashiko
  Cc: opendmb, florian.fainelli, bcm-kernel-feedback-list,
	andrew+netdev, davem, edumazet, kuba, pabeni, justin.chen,
	netdev, linux-kernel

Hi (S|Cl)ashiko

On 22.9.2026 14:47, netdev-bot+sashiko@kernel.org wrote:
> 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] Incomplete fix: the Wake-on-LAN branch of bcmgenet_suspend()
>   keeps the exact queue-stop-before-Tx-NAPI-stop ordering that this…
> 
> --
> 
> net: bcmgenet: stop Tx NAPI before disabling the queues
> 
> This moves bcmgenet_disable_tx_napi() ahead of netif_tx_disable() in
> bcmgenet_netif_stop(), so an in-flight Tx completion can no longer call
> netif_tx_wake_queue() after the queues have been stopped and before
> bcmgenet_fini_dma() frees the rings.
> 
>> diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c 
>> b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
>> index b916080f4ff17..0789b60e664ad 100644
>> --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c
>> +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c
>> @@ -3441,6 +3441,8 @@ static void bcmgenet_netif_stop(struct 
>> net_device *dev, bool stop_phy)
>>  {
>>  	struct bcmgenet_priv *priv = netdev_priv(dev);
>> 
>> +	/* Stop completion polling before it can wake a stopped queue */
>> +	bcmgenet_disable_tx_napi(priv);
>>  	netif_tx_disable(dev);
> 
> [Severity: High]
> 
> The commit message says:
> 
>   Close is safe because dev_deactivate_many() stops the qdisc first.
>   bcmgenet_suspend() does not, so stop Tx NAPI before the queues.
> 
> bcmgenet_suspend() only reaches bcmgenet_netif_stop() in the non-WoL 
> else
> branch. Does the Wake-on-LAN branch still have the ordering this patch
> declares unsafe?

Yes, and the window is even wider: bcmgenet_tdma_disable() spins up to
5 ms (DMA_TIMEOUT_VAL), and nothing re-stops the queue afterwards.

The reordering makes sense to me, so I will send a v2.

> 
> drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_suspend() {
> 	if (device_may_wakeup(d) && priv->wolopts) {
> 		netif_tx_disable(dev);
> 		...
> 		/* HFB programming, interrupt clearing */
> 		...
> 		if (-ETIMEDOUT == bcmgenet_tdma_disable(priv))
> 			netdev_warn(priv->dev,
> 				    "Timed out while disabling TX DMA\n");
> 
> 		bcmgenet_disable_tx_napi(priv);
> 		bcmgenet_disable_rx_napi(priv);
> 		disable_irq(priv->irq1);
> 		bcmgenet_tx_reclaim_all(dev);
> 		bcmgenet_fini_tx_napi(priv);
> 	} else {
> 		/* Teardown the interface */
> 		bcmgenet_netif_stop(dev, true);
> 	}
> }
> 
> Between netif_tx_disable() and bcmgenet_disable_tx_napi() here, 
> priv->irq1
> is still enabled and the Tx NAPI is still live, and 
> bcmgenet_tdma_disable()
> spins in udelay, so the window is not short. Can bcmgenet_tx_poll() run 
> in
> that window and undo the stop?
> 
> drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_tx_poll() {
> 	work_done = __bcmgenet_tx_reclaim(ring->priv->dev, ring);
> 	if (ring->free_bds > (MAX_SKB_FRAGS + 1)) {
> 		txq = netdev_get_tx_queue(ring->priv->dev, ring->index);
> 		netif_tx_wake_queue(txq);
> 	}
> }
> 
> Nothing later in the WoL branch re-stops the queue, and suspend does 
> not
> call dev_deactivate(), so the qdisc stays attached. sch_direct_xmit() 
> gates
> only on the per-txq stop bit, not on netif_device_present(), so the
> netif_device_detach() done earlier in bcmgenet_suspend() does not block 
> a
> transmit:
> 
> net/sched/sch_generic.c:sch_direct_xmit() {
> 	HARD_TX_LOCK(dev, txq, smp_processor_id());
> 	if (!netif_xmit_frozen_or_stopped(txq))
> 		skb = dev_hard_start_xmit(skb, dev, txq, &ret);
> }
> 
> A kernel-generated transmit is enough to hit this (ARP/ND reply, TCP
> retransmit, forwarding), so freezing user space does not close it.
> 
> Two follow-on questions about that path:
> 
> Can this leak the skb and its DMA mapping? A transmit arriving after
> bcmgenet_tx_reclaim_all() dma_map_single()s the skb into ring->cbs 
> while
> TDMA is already disabled, so no completion reclaims it. On the 
> WoL-success
> resume path, bcmgenet_resume() -> bcmgenet_init_tx_queues() ->
> bcmgenet_init_tx_ring() resets the software and hardware indices 
> without
> walking cbs:
> 
> drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_init_tx_ring() 
> {
> 	ring->cbs = priv->tx_cbs + start_ptr;
> 	ring->size = size;
> 	ring->clean_ptr = start_ptr;
> 	ring->c_index = 0;
> 	ring->free_bds = size;
> 	ring->write_ptr = start_ptr;
> 	ring->cb_ptr = start_ptr;
> 	ring->prod_index = 0;
> }
> 
> Neither that function nor the resume path calls 
> netdev_tx_reset_queue(), so
> does the netdev_tx_sent_queue() accounting from the stray xmit leave 
> BQL
> skewed as well?

AFAIU this should be fixed with the reordering suggest above.

> 
> And on the WoL-failure resume path, can this reproduce the same
> use-after-free quoted in the commit message?
> 
> drivers/net/ethernet/broadcom/genet/bcmgenet.c:bcmgenet_resume() {
> 		/* MAC was reset so complete bcmgenet_netif_stop() */
> 		umac_enable_set(priv, CMD_RX_EN | CMD_TX_EN, false);
> 		bcmgenet_rdma_disable(priv);
> 		bcmgenet_intr_disable(priv);
> 		bcmgenet_fini_dma(priv);
> }
> 
> bcmgenet_fini_dma() kfree()s priv->tx_cbs while the Tx queue is still
> runnable, and bcmgenet_init_dma() only reallocates it after 
> init_umac(),
> phy_init_hw() and genphy_config_aneg(), so bcmgenet_xmit() ->
> bcmgenet_get_txcb() would write through ring->cbs into freed memory.

Same as above.

> 
> Would it make sense to reorder the WoL branch as well, moving
> bcmgenet_disable_tx_napi() ahead of netif_tx_disable() there?

Yes, makes sense and should fix all of the above.

pw-bot: cr

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

end of thread, other threads:[~2026-09-22 12:58 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-09-21 12:44 [PATCH net] net: bcmgenet: stop Tx NAPI before disabling the queues Nicolai Buchwitz
2026-09-22 12:47 ` netdev-bot+sashiko
2026-09-22 12:58   ` Nicolai Buchwitz

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®