net: stmmac: fix NULL pointer dereference in stmmac_get_tx_hwtstamp
authordamuzi000 <damuzi000@gmail.com>
Fri, 17 Jan 2014 15:47:59 +0000 (23:47 +0800)
committerDavid S. Miller <davem@davemloft.net>
Mon, 20 Jan 2014 04:02:45 +0000 (20:02 -0800)
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>
drivers/net/ethernet/stmicro/stmmac/chain_mode.c
drivers/net/ethernet/stmicro/stmmac/ring_mode.c
drivers/net/ethernet/stmicro/stmmac/stmmac_main.c

index d234ab5..72d282b 100644 (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);
 
        while (len != 0) {
+               priv->tx_skbuff[entry] = NULL;
                entry = (++priv->cur_tx) % txsize;
                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,
                                                        STMMAC_CHAIN_MODE);
                        priv->hw->desc->set_tx_owner(desc);
-                       priv->tx_skbuff[entry] = NULL;
                        len -= bmax;
                        i++;
                } 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,
                                                        STMMAC_CHAIN_MODE);
                        priv->hw->desc->set_tx_owner(desc);
-                       priv->tx_skbuff[entry] = NULL;
                        len = 0;
                }
        }
index 1ef9d8a..a96c7c2 100644 (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,
                                                STMMAC_RING_MODE);
                wmb();
+               priv->tx_skbuff[entry] = NULL;
                entry = (++priv->cur_tx) % txsize;
 
                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);
                wmb();
                priv->hw->desc->set_tx_owner(desc);
-               priv->tx_skbuff[entry] = NULL;
        } else {
                desc->des2 = dma_map_single(priv->device, skb->data,
                                            nopaged_len, DMA_TO_DEVICE);
index 0c5c120..5db91be 100644 (file)
@@ -334,7 +334,7 @@ static void stmmac_get_tx_hwtstamp(struct stmmac_priv *priv,
                return;
 
        /* 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;
 
        if (priv->adv_ts)
@@ -1081,21 +1081,24 @@ static void dma_free_tx_skbufs(struct stmmac_priv *priv)
        int i;
 
        for (i = 0; i < priv->dma_tx_size; i++) {
-               if (priv->tx_skbuff[i] != NULL) {
-                       struct dma_desc *p;
-                       if (priv->extend_desc)
-                               p = &((priv->dma_etx + i)->basic);
-                       else
-                               p = priv->dma_tx + i;
+               struct dma_desc *p;
 
-                       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);
+               if (priv->extend_desc)
+                       p = &((priv->dma_etx + i)->basic);
+               else
+                       p = priv->dma_tx + i;
+
+               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]);
                        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;
 
-       priv->tx_skbuff[entry] = skb;
-
        /* To program the descriptors according to the size of the frame */
        if (priv->mode == STMMAC_RING_MODE) {
                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];
                int len = skb_frag_size(frag);
 
+               priv->tx_skbuff[entry] = NULL;
                entry = (++priv->cur_tx) % txsize;
                if (priv->extend_desc)
                        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,
                                              DMA_TO_DEVICE);
                priv->tx_skbuff_dma[entry] = desc->des2;
-               priv->tx_skbuff[entry] = NULL;
                priv->hw->desc->prepare_tx_desc(desc, 0, len, csum_insertion,
                                                priv->mode);
                wmb();
@@ -1913,6 +1914,8 @@ static netdev_tx_t stmmac_xmit(struct sk_buff *skb, struct net_device *dev)
                wmb();
        }
 
+       priv->tx_skbuff[entry] = skb;
+
        /* Finalize the latest segment. */
        priv->hw->desc->close_tx_desc(desc);