p54: fix oops on faulty devices
authorChristian Lamparter <chunkeey@web.de>
Tue, 9 Dec 2008 20:07:50 +0000 (21:07 +0100)
committerJohn W. Linville <linville@tuxdriver.com>
Fri, 12 Dec 2008 19:02:15 +0000 (14:02 -0500)
This patch fixes an oops when the devices suddenly starts
to receive martian data frames.

bug reference:
http://marc.info/?l=linux-wireless&m=122872280317635&w=2

Signed-off-by: Christian Lamparter <chunkeey@web.de>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
drivers/net/wireless/p54/p54common.c

index fac6b416e9e9572806be44413c58a3c115fecc40..a4e99b02af021b81f7061d4659741df6ef86860e 100644 (file)
@@ -540,6 +540,14 @@ static int p54_rx_data(struct ieee80211_hw *dev, struct sk_buff *skb)
        size_t header_len = sizeof(*hdr);
        u32 tsf32;
 
+       /*
+        * If the device is in a unspecified state we have to
+        * ignore all data frames. Else we could end up with a
+        * nasty crash.
+        */
+       if (unlikely(priv->mode == NL80211_IFTYPE_UNSPECIFIED))
+               return 0;
+
        if (!(hdr->flags & cpu_to_le16(P54_HDR_FLAG_DATA_IN_FCS_GOOD))) {
                if (priv->filter_flags & FIF_FCSFAIL)
                        rx_status.flag |= RX_FLAG_FAILED_FCS_CRC;
@@ -608,6 +616,12 @@ void p54_free_skb(struct ieee80211_hw *dev, struct sk_buff *skb)
        if (unlikely(!skb || !dev || !skb_queue_len(&priv->tx_queue)))
                return;
 
+       /*
+        * don't try to free an already unlinked skb
+        */
+       if (unlikely((!skb->next) || (!skb->prev)))
+               return;
+
        spin_lock_irqsave(&priv->tx_queue.lock, flags);
        info = IEEE80211_SKB_CB(skb);
        range = (void *)info->rate_driver_data;
@@ -1695,19 +1709,18 @@ static void p54_stop(struct ieee80211_hw *dev)
        struct sk_buff *skb;
 
        mutex_lock(&priv->conf_mutex);
+       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
        del_timer(&priv->stats_timer);
        p54_free_skb(dev, priv->cached_stats);
        priv->cached_stats = NULL;
        if (priv->cached_beacon)
                p54_tx_cancel(dev, priv->cached_beacon);
 
+       priv->stop(dev);
        while ((skb = skb_dequeue(&priv->tx_queue)))
                kfree_skb(skb);
-
        priv->cached_beacon = NULL;
-       priv->stop(dev);
        priv->tsf_high32 = priv->tsf_low32 = 0;
-       priv->mode = NL80211_IFTYPE_UNSPECIFIED;
        mutex_unlock(&priv->conf_mutex);
 }