public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH net] net: macb: drop in-flight Tx SKBs on close
@ 2026-04-24 10:01 Théo Lebrun
  2026-04-24 10:30 ` Nicolai Buchwitz
  0 siblings, 1 reply; 3+ messages in thread
From: Théo Lebrun @ 2026-04-24 10:01 UTC (permalink / raw)
  To: Nicolas Ferre, Claudiu Beznea, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Haavard Skinnemoen,
	Jeff Garzik
  Cc: Paolo Valerio, Conor Dooley, Nicolai Buchwitz, netdev,
	linux-kernel, Vladimir Kondratiev, Gregory CLEMENT,
	Benoît Monin, Tawfik Bayouk, Thomas Petazzoni,
	Maxime Chevallier, Théo Lebrun

The MACB driver has since forever leaked the outgoing SKBs that have not
yet been marked as completed. They live in queue->tx_skb which gets
freed without remorse nor checking.

macb_free_consistent() gets called in a few codepaths, but only
close will trigger the added expressions. In macb_open() and
macb_alloc_consistent() failure cases, tx_skb just got allocated
and is empty.

Fixes: 89e5785fc8a6 ("[PATCH] Atmel MACB ethernet driver")
Signed-off-by: Théo Lebrun <theo.lebrun@bootlin.com>
---
 drivers/net/ethernet/cadence/macb_main.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c
index a12aa21244e8..3ffd60b852f8 100644
--- a/drivers/net/ethernet/cadence/macb_main.c
+++ b/drivers/net/ethernet/cadence/macb_main.c
@@ -2649,6 +2649,7 @@ static unsigned int macb_rx_ring_size_per_queue(struct macb *bp)
 static void macb_free_consistent(struct macb *bp)
 {
 	struct device *dev = &bp->pdev->dev;
+	unsigned int dropped, tail;
 	struct macb_queue *queue;
 	unsigned int q;
 	size_t size;
@@ -2668,6 +2669,14 @@ static void macb_free_consistent(struct macb *bp)
 	dma_free_coherent(dev, size, bp->queues[0].rx_ring, bp->queues[0].rx_ring_dma);
 
 	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
+		dropped = CIRC_CNT(queue->tx_head, queue->tx_tail,
+				   bp->tx_ring_size);
+		queue->stats.tx_dropped += dropped;
+		bp->dev->stats.tx_dropped += dropped;
+
+		for (tail = queue->tx_tail; tail != queue->tx_head; tail++)
+			macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
+
 		kfree(queue->tx_skb);
 		queue->tx_skb = NULL;
 		queue->tx_ring = NULL;

---
base-commit: 41517b5a932a35c6f59a997ab3b7fa7746c273bd
change-id: 20260423-macb-drop-tx-f9ce72720d05

Best regards,
--  
Théo Lebrun <theo.lebrun@bootlin.com>


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

* Re: [PATCH net] net: macb: drop in-flight Tx SKBs on close
  2026-04-24 10:01 [PATCH net] net: macb: drop in-flight Tx SKBs on close Théo Lebrun
@ 2026-04-24 10:30 ` Nicolai Buchwitz
  2026-04-24 11:45   ` Théo Lebrun
  0 siblings, 1 reply; 3+ messages in thread
From: Nicolai Buchwitz @ 2026-04-24 10:30 UTC (permalink / raw)
  To: Théo Lebrun
  Cc: Nicolas Ferre, Claudiu Beznea, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Haavard Skinnemoen,
	Jeff Garzik, Paolo Valerio, Conor Dooley, netdev, linux-kernel,
	Vladimir Kondratiev, Gregory CLEMENT, Benoît Monin,
	Tawfik Bayouk, Thomas Petazzoni, Maxime Chevallier

Hi Théo,

thanks for your patch.

On 24.4.2026 12:01, Théo Lebrun wrote:
> [...]

> 
>  	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
> +		dropped = CIRC_CNT(queue->tx_head, queue->tx_tail,
> +				   bp->tx_ring_size);
> +		queue->stats.tx_dropped += dropped;
> +		bp->dev->stats.tx_dropped += dropped;

AFAIUI CIRC_CNT counts descriptor slots, not packets. A fragmented
skb uses multiple slots so tx_dropped would be overcounted?

> +
> +		for (tail = queue->tx_tail; tail != queue->tx_head; tail++)
> +			macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);

I might be missing something, but couldn't this crash on the
macb_alloc_consistent() -> out_err path after a previous close
with in-flight frames?

1. First close: the new loop runs and frees skbs, but tx_head and
    tx_tail are not reset. kfree(tx_skb) sets it to NULL.
2. Second open: macb_alloc_consistent() fails early (e.g. the tx
    dma_alloc_coherent on the first line) and jumps to out_err.
3. macb_free_consistent() runs again. CIRC_CNT is non-zero (stale
    from previous session). macb_tx_skb() dereferences queue->tx_skb
    which is NULL.

Or if the failure happens later, the loop would iterate over a
freshly kmalloc'd (uninitialized) tx_skb array and macb_tx_unmap()
would read garbage mapping/skb pointers.

Maybe reset tx_head = tx_tail = 0 after the loop, or guard with
if (queue->tx_skb)?

> +
>  		kfree(queue->tx_skb);
>  		queue->tx_skb = NULL;
>  		queue->tx_ring = NULL;
> 

> [...]

Thanks,
Nicolai

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

* Re: [PATCH net] net: macb: drop in-flight Tx SKBs on close
  2026-04-24 10:30 ` Nicolai Buchwitz
@ 2026-04-24 11:45   ` Théo Lebrun
  0 siblings, 0 replies; 3+ messages in thread
From: Théo Lebrun @ 2026-04-24 11:45 UTC (permalink / raw)
  To: Nicolai Buchwitz
  Cc: Nicolas Ferre, Claudiu Beznea, Andrew Lunn, David S. Miller,
	Eric Dumazet, Jakub Kicinski, Paolo Abeni, Haavard Skinnemoen,
	Jeff Garzik, Paolo Valerio, Conor Dooley, netdev, linux-kernel,
	Vladimir Kondratiev, Gregory CLEMENT, Benoît Monin,
	Tawfik Bayouk, Thomas Petazzoni, Maxime Chevallier

Hello Nicolai,

On Fri Apr 24, 2026 at 12:30 PM CEST, Nicolai Buchwitz wrote:
> On 24.4.2026 12:01, Théo Lebrun wrote:
>> [...]
>
>> 
>>  	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
>> +		dropped = CIRC_CNT(queue->tx_head, queue->tx_tail,
>> +				   bp->tx_ring_size);
>> +		queue->stats.tx_dropped += dropped;
>> +		bp->dev->stats.tx_dropped += dropped;
>
> AFAIUI CIRC_CNT counts descriptor slots, not packets. A fragmented
> skb uses multiple slots so tx_dropped would be overcounted?

Ah yes, agreed. dropped is the count of !!macb_tx_skb(queue, tail)->skb.

>> +		for (tail = queue->tx_tail; tail != queue->tx_head; tail++)
>> +			macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
>
> I might be missing something, but couldn't this crash on the
> macb_alloc_consistent() -> out_err path after a previous close
> with in-flight frames?
>
> 1. First close: the new loop runs and frees skbs, but tx_head and
>     tx_tail are not reset. kfree(tx_skb) sets it to NULL.
> 2. Second open: macb_alloc_consistent() fails early (e.g. the tx
>     dma_alloc_coherent on the first line) and jumps to out_err.
> 3. macb_free_consistent() runs again. CIRC_CNT is non-zero (stale
>     from previous session). macb_tx_skb() dereferences queue->tx_skb
>     which is NULL.
>
> Or if the failure happens later, the loop would iterate over a
> freshly kmalloc'd (uninitialized) tx_skb array and macb_tx_unmap()
> would read garbage mapping/skb pointers.
>
> Maybe reset tx_head = tx_tail = 0 after the loop, or guard with
> if (queue->tx_skb)?

Ah yes, guarding on queue->tx_skb sounds good for the error case of
macb_alloc_coherent(). I didn't notice we could land in free with
queue->tx_skb NULL. But it is also surprising to keep stall head/tail
cursors. I'll probably reset them as well for V2.

So along the lines of:

static void macb_free_consistent(struct macb *bp)
{
	// ... as before ...

	for (q = 0, queue = bp->queues; q < bp->num_queues; ++q, ++queue) {
		if (queue->tx_skb) {
			for (tail = queue->tx_tail; tail != queue->tx_head; tail++) {
				if (macb_tx_skb(queue, tail)->skb)
					dropped++;
				macb_tx_unmap(bp, macb_tx_skb(queue, tail), 0);
			}

			queue->stats.tx_dropped += dropped;
			bp->dev->stats.tx_dropped += dropped;

		}

		kfree(queue->tx_skb);
		queue->tx_skb = NULL;
		queue->tx_head = 0;
		queue->tx_tail = 0;
		queue->tx_ring = NULL;
		queue->rx_ring = NULL;
	}
}

Thanks for the swift review!

--
Théo Lebrun, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com


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

end of thread, other threads:[~2026-04-24 11:46 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-04-24 10:01 [PATCH net] net: macb: drop in-flight Tx SKBs on close Théo Lebrun
2026-04-24 10:30 ` Nicolai Buchwitz
2026-04-24 11:45   ` Théo Lebrun

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