Thread (65 messages) flat view 65 messages, 6 authors, 2020-04-29

RE: [PATCH 03/14] net: axienet: Fix DMA descriptor cleanup path

From: Radhey Shyam Pandey <hidden>
Date: 2020-01-10 17:05:51
Also in: linux-arm-kernel, lkml

-----Original Message-----
From: Andre Przywara <andre.przywara@arm.com>
Sent: Friday, January 10, 2020 9:13 PM
To: Radhey Shyam Pandey <redacted>
Cc: David S . Miller <davem@davemloft.net>; Michal Simek
[off-list ref]; Robert Hancock [off-list ref];
netdev@vger.kernel.org; linux-arm-kernel@lists.infradead.org; linux-
kernel@vger.kernel.org
Subject: Re: [PATCH 03/14] net: axienet: Fix DMA descriptor cleanup path

On Fri, 10 Jan 2020 15:14:46 +0000
Radhey Shyam Pandey [off-list ref] wrote:

Hi Radhey,

thanks for having a look!
quoted
quoted
-----Original Message-----
From: Andre Przywara <andre.przywara@arm.com>
Sent: Friday, January 10, 2020 5:24 PM
To: David S . Miller <davem@davemloft.net>; Radhey Shyam Pandey
[off-list ref]
Cc: Michal Simek <redacted>; Robert Hancock
[off-list ref]; netdev@vger.kernel.org; linux-arm-
kernel@lists.infradead.org; linux-kernel@vger.kernel.org
Subject: [PATCH 03/14] net: axienet: Fix DMA descriptor cleanup path

When axienet_dma_bd_init() bails out during the initialisation process,
it might do so with parts of the structure already allocated and
initialised, while other parts have not been touched yet. Before
returning in this case, we call axienet_dma_bd_release(), which does not
take care of this corner case.
This is most obvious by the first loop happily dereferencing
lp->rx_bd_v, which we actually check to be non NULL *afterwards*.

Make sure we only unmap or free already allocated structures, by:
- directly returning with -ENOMEM if nothing has been allocated at all
- checking for lp->rx_bd_v to be non-NULL *before* using it
- only unmapping allocated DMA RX regions

This avoids NULL pointer dereferences when initialisation fails.

Signed-off-by: Andre Przywara <andre.przywara@arm.com>
---
 .../net/ethernet/xilinx/xilinx_axienet_main.c | 43 ++++++++++++-------
 1 file changed, 28 insertions(+), 15 deletions(-)
diff --git a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
index 97482cf093ce..7e90044cf2d9 100644
--- a/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
+++ b/drivers/net/ethernet/xilinx/xilinx_axienet_main.c
@@ -160,24 +160,37 @@ static void axienet_dma_bd_release(struct
net_device *ndev)
 	int i;
 	struct axienet_local *lp = netdev_priv(ndev);

+	/* If we end up here, tx_bd_v must have been DMA allocated. */
+	dma_free_coherent(ndev->dev.parent,
+			  sizeof(*lp->tx_bd_v) * lp->tx_bd_num,
+			  lp->tx_bd_v,
+			  lp->tx_bd_p);
+
+	if (!lp->rx_bd_v)
+		return;
+
 	for (i = 0; i < lp->rx_bd_num; i++) {
-		dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys,
-				 lp->max_frm_size, DMA_FROM_DEVICE);
+		/* A NULL skb means this descriptor has not been initialised
+		 * at all.
+		 */
+		if (!lp->rx_bd_v[i].skb)
+			break;
+
 		dev_kfree_skb(lp->rx_bd_v[i].skb);
-	}

-	if (lp->rx_bd_v) {
-		dma_free_coherent(ndev->dev.parent,
-				  sizeof(*lp->rx_bd_v) * lp->rx_bd_num,
-				  lp->rx_bd_v,
-				  lp->rx_bd_p);
-	}
-	if (lp->tx_bd_v) {
-		dma_free_coherent(ndev->dev.parent,
-				  sizeof(*lp->tx_bd_v) * lp->tx_bd_num,
-				  lp->tx_bd_v,
-				  lp->tx_bd_p);
+		/* For each descriptor, we programmed cntrl with the (non-
zero)
+		 * descriptor size, after it had been successfully allocated.
+		 * So a non-zero value in there means we need to unmap it.
+		 */
quoted
+		if (lp->rx_bd_v[i].cntrl)
I think it should ok to unmap w/o any check?
Do you mean because .phys would be 0 if not initialised? AFAIK 0 can be a
valid DMA address, so there is no special check for that, and unmapping
DMA address 0 will probably go wrong at some point. So it's unlike
kfree(NULL).
I mean if skb allocation is successful in _dma_bd_init then in release path
we can assume .phys is always a valid address and skip rx_bd_v[i].cntrl
check. 
Cheers,
Andre.

quoted
quoted
+			dma_unmap_single(ndev->dev.parent, lp-
quoted
rx_bd_v[i].phys,
+					 lp->max_frm_size,
DMA_FROM_DEVICE);
 	}
+
+	dma_free_coherent(ndev->dev.parent,
+			  sizeof(*lp->rx_bd_v) * lp->rx_bd_num,
+			  lp->rx_bd_v,
+			  lp->rx_bd_p);
 }

 /**
@@ -207,7 +220,7 @@ static int axienet_dma_bd_init(struct net_device
*ndev)
 					 sizeof(*lp->tx_bd_v) * lp-
quoted
tx_bd_num,
 					 &lp->tx_bd_p, GFP_KERNEL);
 	if (!lp->tx_bd_v)
-		goto out;
+		return -ENOMEM;

 	lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
 					 sizeof(*lp->rx_bd_v) * lp-
quoted
rx_bd_num,
--
2.17.1
  
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help