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-20 18:32:14
Also in: lkml, netdev

 -----Original Message-----
From: Andre Przywara <andre.przywara@arm.com>
Sent: Thursday, January 16, 2020 11:33 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 17:05:45 +0000
Radhey Shyam Pandey [off-list ref] wrote:

Hi,
quoted
quoted
-----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.
*/
quoted
quoted
quoted
quoted
+	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
quoted
quoted
quoted
quoted
+		 * 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-
quoted
quoted
quoted
quoted
zero)
+		 * descriptor size, after it had been successfully
allocated.
quoted
quoted
quoted
quoted
+		 * So a non-zero value in there means we need to
unmap it.
quoted
quoted
quoted
quoted
+		 */
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.
I don't think we can assume this. If the skb allocation succeeded, but then the
dma_map_single failed (which we check with dma_mapping_error()), we would
end up with a valid skb, but an uninitialised phys DMA address in the registers.
That's why I set .cntrl only after having checked the dma_map_single() result.

Or am I missing something?
Got it. Looks fine then.
Cheers,
Andre
quoted
quoted
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

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help