brcmfmac: use skb_cow() in brcmf_sdbrcm_txpkt() to assure alignment
authorArend van Spriel <arend@broadcom.com>
Wed, 3 Apr 2013 10:40:28 +0000 (12:40 +0200)
committerJohn W. Linville <linville@tuxdriver.com>
Wed, 3 Apr 2013 19:07:03 +0000 (15:07 -0400)
In brcmf_sdbrcm_txpkt() a new packet is allocated and used to transmit
to firmware freeing up the original packet. However, that packet is
still referenced in firmware-signalling so this would result in a
double free. Using skb_cow() avoids this as the packet reference is
unchanged.

Reviewed-by: Hante Meuleman <meuleman@broadcom.com>
Reviewed-by: Piotr Haber <phaber@broadcom.com>
Reviewed-by: Pieter-Paul Giesberts <pieterpg@broadcom.com>
Signed-off-by: Arend van Spriel <arend@broadcom.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c

index 4fa19b9..f5f04ba 100644 (file)
@@ -1781,7 +1781,6 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
        u8 *frame;
        u16 len, pad = 0;
        u32 swheader;
-       struct sk_buff *new;
        int i;
 
        brcmf_dbg(TRACE, "Enter\n");
@@ -1795,26 +1794,14 @@ static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
                        brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
                                  skb_headroom(pkt), pad);
                        bus->sdiodev->bus_if->tx_realloc++;
-                       new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN);
-                       if (!new) {
-                               brcmf_err("couldn't allocate new %d-byte packet\n",
-                                         pkt->len + BRCMF_SDALIGN);
-                               ret = -ENOMEM;
+                       ret = skb_cow(pkt, BRCMF_SDALIGN);
+                       if (ret)
                                goto done;
-                       }
-
-                       pkt_align(new, pkt->len, BRCMF_SDALIGN);
-                       memcpy(new->data, pkt->data, pkt->len);
-                       brcmu_pkt_buf_free_skb(pkt);
-                       pkt = new;
-                       frame = (u8 *) (pkt->data);
-                       /* precondition: (frame % BRCMF_SDALIGN) == 0) */
-                       pad = 0;
-               } else {
-                       skb_push(pkt, pad);
-                       frame = (u8 *) (pkt->data);
-                       memset(frame + SDPCM_HDRLEN, 0, pad);
+                       pad = ((unsigned long)frame % BRCMF_SDALIGN);
                }
+               skb_push(pkt, pad);
+               frame = (u8 *) (pkt->data);
+               memset(frame, 0, pad + SDPCM_HDRLEN);
        }
        /* precondition: pad < BRCMF_SDALIGN */