Commit f26667a3 authored by Andre Przywara's avatar Andre Przywara Committed by David S. Miller

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: default avatarAndre Przywara <andre.przywara@arm.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent ee44d0b7
...@@ -160,24 +160,37 @@ static void axienet_dma_bd_release(struct net_device *ndev) ...@@ -160,24 +160,37 @@ static void axienet_dma_bd_release(struct net_device *ndev)
int i; int i;
struct axienet_local *lp = netdev_priv(ndev); 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++) { for (i = 0; i < lp->rx_bd_num; i++) {
dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys, /* A NULL skb means this descriptor has not been initialised
lp->max_frm_size, DMA_FROM_DEVICE); * at all.
*/
if (!lp->rx_bd_v[i].skb)
break;
dev_kfree_skb(lp->rx_bd_v[i].skb); dev_kfree_skb(lp->rx_bd_v[i].skb);
}
if (lp->rx_bd_v) { /* For each descriptor, we programmed cntrl with the (non-zero)
dma_free_coherent(ndev->dev.parent, * descriptor size, after it had been successfully allocated.
sizeof(*lp->rx_bd_v) * lp->rx_bd_num, * So a non-zero value in there means we need to unmap it.
lp->rx_bd_v, */
lp->rx_bd_p); if (lp->rx_bd_v[i].cntrl)
} dma_unmap_single(ndev->dev.parent, lp->rx_bd_v[i].phys,
if (lp->tx_bd_v) { lp->max_frm_size, DMA_FROM_DEVICE);
dma_free_coherent(ndev->dev.parent,
sizeof(*lp->tx_bd_v) * lp->tx_bd_num,
lp->tx_bd_v,
lp->tx_bd_p);
} }
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) ...@@ -207,7 +220,7 @@ static int axienet_dma_bd_init(struct net_device *ndev)
sizeof(*lp->tx_bd_v) * lp->tx_bd_num, sizeof(*lp->tx_bd_v) * lp->tx_bd_num,
&lp->tx_bd_p, GFP_KERNEL); &lp->tx_bd_p, GFP_KERNEL);
if (!lp->tx_bd_v) if (!lp->tx_bd_v)
goto out; return -ENOMEM;
lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent, lp->rx_bd_v = dma_alloc_coherent(ndev->dev.parent,
sizeof(*lp->rx_bd_v) * lp->rx_bd_num, sizeof(*lp->rx_bd_v) * lp->rx_bd_num,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment