net: stmmac: fix NULL pointer dereference in stmmac_get_tx_hwtstamp

When timestamping is enabled, stmmac_tx_clean will call
stmmac_get_tx_hwtstamp to get tx TS.
But the skb can be NULL because the last of its tx_skbuff is NULL
if this packet frame is filled in more than one descriptors.

To fix the issue, change the code:
- Store TX skb to the tx_skbuff[] of frame's last segment.
- Check skb is not NULL in stmmac_get_tx_hwtstamp.

Signed-off-by: Bruce Liu <damuzi000@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
damuzi000 2014-01-17 23:47:59 +08:00 committed by David S. Miller
parent af0bd4e9ba
commit 75e4364f67
3 changed files with 21 additions and 19 deletions

View File

@ -51,6 +51,7 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE); priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, STMMAC_CHAIN_MODE);
while (len != 0) { while (len != 0) {
priv->tx_skbuff[entry] = NULL;
entry = (++priv->cur_tx) % txsize; entry = (++priv->cur_tx) % txsize;
desc = priv->dma_tx + entry; desc = priv->dma_tx + entry;
@ -62,7 +63,6 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum, priv->hw->desc->prepare_tx_desc(desc, 0, bmax, csum,
STMMAC_CHAIN_MODE); STMMAC_CHAIN_MODE);
priv->hw->desc->set_tx_owner(desc); priv->hw->desc->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
len -= bmax; len -= bmax;
i++; i++;
} else { } else {
@ -73,7 +73,6 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum, priv->hw->desc->prepare_tx_desc(desc, 0, len, csum,
STMMAC_CHAIN_MODE); STMMAC_CHAIN_MODE);
priv->hw->desc->set_tx_owner(desc); priv->hw->desc->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
len = 0; len = 0;
} }
} }

View File

@ -58,6 +58,7 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum, priv->hw->desc->prepare_tx_desc(desc, 1, bmax, csum,
STMMAC_RING_MODE); STMMAC_RING_MODE);
wmb(); wmb();
priv->tx_skbuff[entry] = NULL;
entry = (++priv->cur_tx) % txsize; entry = (++priv->cur_tx) % txsize;
if (priv->extend_desc) if (priv->extend_desc)
@ -73,7 +74,6 @@ static unsigned int stmmac_jumbo_frm(void *p, struct sk_buff *skb, int csum)
STMMAC_RING_MODE); STMMAC_RING_MODE);
wmb(); wmb();
priv->hw->desc->set_tx_owner(desc); priv->hw->desc->set_tx_owner(desc);
priv->tx_skbuff[entry] = NULL;
} else { } else {
desc->des2 = dma_map_single(priv->device, skb->data, desc->des2 = dma_map_single(priv->device, skb->data,
nopaged_len, DMA_TO_DEVICE); nopaged_len, DMA_TO_DEVICE);

View File

@ -334,7 +334,7 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
return; return;
/* exit if skb doesn't support hw tstamp */ /* exit if skb doesn't support hw tstamp */
if (likely(!(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS))) if (likely(!skb || !(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS)))
return; return;
if (priv->adv_ts) if (priv->adv_ts)
@ -1081,21 +1081,24 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv)
int i; int i;
for (i = 0; i < priv->dma_tx_size; i++) { for (i = 0; i < priv->dma_tx_size; i++) {
if (priv->tx_skbuff[i] != NULL) { struct dma_desc *p;
struct dma_desc *p;
if (priv->extend_desc)
p = &((priv->dma_etx + i)->basic);
else
p = priv->dma_tx + i;
if (priv->tx_skbuff_dma[i]) if (priv->extend_desc)
dma_unmap_single(priv->device, p = &((priv->dma_etx + i)->basic);
priv->tx_skbuff_dma[i], else
priv->hw->desc->get_tx_len(p), p = priv->dma_tx + i;
DMA_TO_DEVICE);
if (priv->tx_skbuff_dma[i]) {
dma_unmap_single(priv->device,
priv->tx_skbuff_dma[i],
priv->hw->desc->get_tx_len(p),
DMA_TO_DEVICE);
priv->tx_skbuff_dma[i] = 0;
}
if (priv->tx_skbuff[i] != NULL) {
dev_kfree_skb_any(priv->tx_skbuff[i]); dev_kfree_skb_any(priv->tx_skbuff[i]);
priv->tx_skbuff[i] = NULL; priv->tx_skbuff[i] = NULL;
priv->tx_skbuff_dma[i] = 0;
} }
} }
} }
@ -1867,8 +1870,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
first = desc; first = desc;
priv->tx_skbuff[entry] = skb;
/* To program the descriptors according to the size of the frame */ /* To program the descriptors according to the size of the frame */
if (priv->mode == STMMAC_RING_MODE) { if (priv->mode == STMMAC_RING_MODE) {
is_jumbo = priv->hw->ring->is_jumbo_frm(skb->len, is_jumbo = priv->hw->ring->is_jumbo_frm(skb->len,
@ -1896,6 +1897,7 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; const skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
int len = skb_frag_size(frag); int len = skb_frag_size(frag);
priv->tx_skbuff[entry] = NULL;
entry = (++priv->cur_tx) % txsize; entry = (++priv->cur_tx) % txsize;
if (priv->extend_desc) if (priv->extend_desc)
desc = (struct dma_desc *)(priv->dma_etx + entry); desc = (struct dma_desc *)(priv->dma_etx + entry);
@ -1905,7 +1907,6 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len, desc->des2 = skb_frag_dma_map(priv->device, frag, 0, len,
DMA_TO_DEVICE); DMA_TO_DEVICE);
priv->tx_skbuff_dma[entry] = desc->des2; priv->tx_skbuff_dma[entry] = desc->des2;
priv->tx_skbuff[entry] = NULL;
priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion, priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion,
priv->mode); priv->mode);
wmb(); wmb();
@ -1913,6 +1914,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
wmb(); wmb();
} }
priv->tx_skbuff[entry] = skb;
/* Finalize the latest segment. */ /* Finalize the latest segment. */
priv->hw->desc->close_tx_desc(desc); priv->hw->desc->close_tx_desc(desc);