With the skb pointer piggy-backed on the TX BD, we have a simple and efficient way to free the skb buffer when the frame has been transmitted. But in order to avoid freeing the skb while there are still fragments from the skb in use, we need to piggy-back on the TX BD of the skb, not the first.
Without this, we are doing use-after-free on the DMA side, when the first BD of a multi TX BD packet is seen as completed in xmit_done, and the remaining BDs are still being processed.
Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Esben Haabendal esben@geanix.com --- drivers/net/ethernet/xilinx/ll_temac_main.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index a1f5f07f4ca9..e82f162cd80c 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -876,7 +876,6 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) return NETDEV_TX_OK; } cur_p->phys = cpu_to_be32(skb_dma_addr); - ptr_to_txbd((void *)skb, cur_p);
for (ii = 0; ii < num_frag; ii++) { if (++lp->tx_bd_tail >= lp->tx_bd_num) @@ -915,6 +914,11 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) } cur_p->app0 |= cpu_to_be32(STS_CTRL_APP0_EOP);
+ /* Mark last fragment with skb address, so it can be consumed + * in temac_start_xmit_done() + */ + ptr_to_txbd((void *)skb, cur_p); + tail_p = lp->tx_bd_p + sizeof(*lp->tx_bd_v) * lp->tx_bd_tail; lp->tx_bd_tail++; if (lp->tx_bd_tail >= lp->tx_bd_num)
Just as the initial check, we need to ensure num_frag+1 buffers available, as that is the number of buffers we are going to use.
This fixes a buffer overflow, which might be seen during heavy network load. Complete lockup of TEMAC was reproducible within about 10 minutes of a particular load.
Fixes: 84823ff80f74 ("net: ll_temac: Fix race condition causing TX hang") Cc: stable@vger.kernel.org # v5.4+ Signed-off-by: Esben Haabendal esben@geanix.com --- drivers/net/ethernet/xilinx/ll_temac_main.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/xilinx/ll_temac_main.c b/drivers/net/ethernet/xilinx/ll_temac_main.c index 9797aa3221d1..cc482ee36501 100644 --- a/drivers/net/ethernet/xilinx/ll_temac_main.c +++ b/drivers/net/ethernet/xilinx/ll_temac_main.c @@ -861,7 +861,7 @@ temac_start_xmit(struct sk_buff *skb, struct net_device *ndev) smp_mb();
/* Space might have just been freed - check again */ - if (temac_check_tx_bd_space(lp, num_frag)) + if (temac_check_tx_bd_space(lp, num_frag + 1)) return NETDEV_TX_BUSY;
netif_wake_queue(ndev);
Hello:
This series was applied to netdev/net.git (refs/heads/master):
On Fri, 18 Jun 2021 12:52:23 +0200 you wrote:
With the skb pointer piggy-backed on the TX BD, we have a simple and efficient way to free the skb buffer when the frame has been transmitted. But in order to avoid freeing the skb while there are still fragments from the skb in use, we need to piggy-back on the TX BD of the skb, not the first.
Without this, we are doing use-after-free on the DMA side, when the first BD of a multi TX BD packet is seen as completed in xmit_done, and the remaining BDs are still being processed.
[...]
Here is the summary with links: - [1/4] net: ll_temac: Make sure to free skb when it is completely used https://git.kernel.org/netdev/net/c/6aa32217a9a4 - [2/4] net: ll_temac: Add memory-barriers for TX BD access https://git.kernel.org/netdev/net/c/28d9fab458b1 - [3/4] net: ll_temac: Fix TX BD buffer overwrite https://git.kernel.org/netdev/net/c/c364df2489b8 - [4/4] net: ll_temac: Avoid ndo_start_xmit returning NETDEV_TX_BUSY https://git.kernel.org/netdev/net/c/f63963411942
You are awesome, thank you! -- Deet-doot-dot, I am a bot. https://korg.docs.kernel.org/patchwork/pwbot.html
linux-stable-mirror@lists.linaro.org