[PATCH] ieee80211: Fixed a kernel oops on module unload

tree 367069f24fc38b4aa910e86ff40094d2078d8aa7
parent a33a1982012e9070736e3717231714dc9892303b
author James Ketrenos <jketreno@linux.intel.com> 1124430800 -0500
committer James Ketrenos <jketreno@linux.intel.com> 1127310571 -0500

Fixed a kernel oops on module unload by adding spin lock protection to
ieee80211's crypt handlers (thanks to Zhu Yi)

Modified scan result logic to report WPA and RSN IEs if set (vs.being
based on wpa_enabled)

Added ieee80211_device as the first parameter to the crypt init()
method.  TKIP modified to use that structure for determining whether to
countermeasures are active.

Signed-off-by: James Ketrenos <jketreno@linux.intel.com>
Signed-off-by: Jeff Garzik <jgarzik@pobox.com>
diff --git a/net/ieee80211/ieee80211_crypt.c b/net/ieee80211/ieee80211_crypt.c
index dc835f6..0c36629 100644
--- a/net/ieee80211/ieee80211_crypt.c
+++ b/net/ieee80211/ieee80211_crypt.c
@@ -41,7 +41,9 @@
 {
 	struct list_head *ptr, *n;
 	struct ieee80211_crypt_data *entry;
+	unsigned long flags;
 
+	spin_lock_irqsave(&ieee->lock, flags);
 	for (ptr = ieee->crypt_deinit_list.next, n = ptr->next;
 	     ptr != &ieee->crypt_deinit_list; ptr = n, n = ptr->next) {
 		entry = list_entry(ptr, struct ieee80211_crypt_data, list);
@@ -57,14 +59,13 @@
 		}
 		kfree(entry);
 	}
+	spin_unlock_irqrestore(&ieee->lock, flags);
 }
 
 void ieee80211_crypt_deinit_handler(unsigned long data)
 {
 	struct ieee80211_device *ieee = (struct ieee80211_device *)data;
-	unsigned long flags;
 
-	spin_lock_irqsave(&ieee->lock, flags);
 	ieee80211_crypt_deinit_entries(ieee, 0);
 	if (!list_empty(&ieee->crypt_deinit_list)) {
 		printk(KERN_DEBUG "%s: entries remaining in delayed crypt "
@@ -72,7 +73,6 @@
 		ieee->crypt_deinit_timer.expires = jiffies + HZ;
 		add_timer(&ieee->crypt_deinit_timer);
 	}
-	spin_unlock_irqrestore(&ieee->lock, flags);
 
 }
 
@@ -182,7 +182,8 @@
 		return NULL;
 }
 
-static void *ieee80211_crypt_null_init(int keyidx)
+static void *ieee80211_crypt_null_init(struct ieee80211_device *ieee,
+				       int keyidx)
 {
 	return (void *)1;
 }
diff --git a/net/ieee80211/ieee80211_crypt_ccmp.c b/net/ieee80211/ieee80211_crypt_ccmp.c
index 7b62908..1e6644b 100644
--- a/net/ieee80211/ieee80211_crypt_ccmp.c
+++ b/net/ieee80211/ieee80211_crypt_ccmp.c
@@ -74,7 +74,7 @@
 	crypto_cipher_encrypt(tfm, &dst, &src, AES_BLOCK_LEN);
 }
 
-static void *ieee80211_ccmp_init(int key_idx)
+static void *ieee80211_ccmp_init(struct ieee80211_device *ieee, int key_idx)
 {
 	struct ieee80211_ccmp_data *priv;
 
diff --git a/net/ieee80211/ieee80211_crypt_tkip.c b/net/ieee80211/ieee80211_crypt_tkip.c
index dca380e..0c495f0 100644
--- a/net/ieee80211/ieee80211_crypt_tkip.c
+++ b/net/ieee80211/ieee80211_crypt_tkip.c
@@ -59,9 +59,11 @@
 
 	/* scratch buffers for virt_to_page() (crypto API) */
 	u8 rx_hdr[16], tx_hdr[16];
+
+	struct ieee80211_device *ieee;
 };
 
-static void *ieee80211_tkip_init(int key_idx)
+static void *ieee80211_tkip_init(struct ieee80211_device *ieee, int key_idx)
 {
 	struct ieee80211_tkip_data *priv;
 
@@ -69,6 +71,9 @@
 	if (priv == NULL)
 		goto fail;
 	memset(priv, 0, sizeof(*priv));
+
+	priv->ieee = ieee;
+
 	priv->key_idx = key_idx;
 
 	priv->tfm_arc4 = crypto_alloc_tfm("arc4", 0);
@@ -264,11 +269,21 @@
 	u32 crc;
 	struct scatterlist sg;
 
+	hdr = (struct ieee80211_hdr *)skb->data;
+
+	if (tkey->ieee->tkip_countermeasures) {
+		if (net_ratelimit()) {
+			printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+			       "TX packet to " MAC_FMT "\n",
+			       tkey->ieee->dev->name, MAC_ARG(hdr->addr1));
+		}
+		return -1;
+	}
+
 	if (skb_headroom(skb) < 8 || skb_tailroom(skb) < 4 ||
 	    skb->len < hdr_len)
 		return -1;
 
-	hdr = (struct ieee80211_hdr *)skb->data;
 	if (!tkey->tx_phase1_done) {
 		tkip_mixing_phase1(tkey->tx_ttak, tkey->key, hdr->addr2,
 				   tkey->tx_iv32);
@@ -325,10 +340,20 @@
 	struct scatterlist sg;
 	int plen;
 
+	hdr = (struct ieee80211_hdr *)skb->data;
+
+	if (tkey->ieee->tkip_countermeasures) {
+		if (net_ratelimit()) {
+			printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
+			       "received packet from " MAC_FMT "\n",
+			       tkey->ieee->dev->name, MAC_ARG(hdr->addr2));
+		}
+		return -1;
+	}
+
 	if (skb->len < hdr_len + 8 + 4)
 		return -1;
 
-	hdr = (struct ieee80211_hdr *)skb->data;
 	pos = skb->data + hdr_len;
 	keyidx = pos[3];
 	if (!(keyidx & (1 << 5))) {
diff --git a/net/ieee80211/ieee80211_crypt_wep.c b/net/ieee80211/ieee80211_crypt_wep.c
index ebe1615..63e783f 100644
--- a/net/ieee80211/ieee80211_crypt_wep.c
+++ b/net/ieee80211/ieee80211_crypt_wep.c
@@ -37,7 +37,7 @@
 	struct crypto_tfm *tfm;
 };
 
-static void *prism2_wep_init(int keyidx)
+static void *prism2_wep_init(struct ieee80211_device *ieee, int keyidx)
 {
 	struct prism2_wep_data *priv;
 
diff --git a/net/ieee80211/ieee80211_rx.c b/net/ieee80211/ieee80211_rx.c
index e0337c8..9a125d4 100644
--- a/net/ieee80211/ieee80211_rx.c
+++ b/net/ieee80211/ieee80211_rx.c
@@ -280,17 +280,6 @@
 	hdr = (struct ieee80211_hdr *)skb->data;
 	hdrlen = ieee80211_get_hdrlen(le16_to_cpu(hdr->frame_ctl));
 
-#ifdef CONFIG_IEEE80211_CRYPT_TKIP
-	if (ieee->tkip_countermeasures && strcmp(crypt->ops->name, "TKIP") == 0) {
-		if (net_ratelimit()) {
-			printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
-			       "received packet from " MAC_FMT "\n",
-			       ieee->dev->name, MAC_ARG(hdr->addr2));
-		}
-		return -1;
-	}
-#endif
-
 	atomic_inc(&crypt->refcnt);
 	res = crypt->ops->decrypt_mpdu(skb, hdrlen, crypt->priv);
 	atomic_dec(&crypt->refcnt);
diff --git a/net/ieee80211/ieee80211_tx.c b/net/ieee80211/ieee80211_tx.c
index 1a09448..435ef5a 100644
--- a/net/ieee80211/ieee80211_tx.c
+++ b/net/ieee80211/ieee80211_tx.c
@@ -157,20 +157,6 @@
 	struct ieee80211_crypt_data *crypt = ieee->crypt[ieee->tx_keyidx];
 	int res;
 
-#ifdef CONFIG_IEEE80211_CRYPT_TKIP
-	struct ieee80211_hdr *header;
-
-	if (ieee->tkip_countermeasures &&
-	    crypt && crypt->ops && strcmp(crypt->ops->name, "TKIP") == 0) {
-		header = (struct ieee80211_hdr *)frag->data;
-		if (net_ratelimit()) {
-			printk(KERN_DEBUG "%s: TKIP countermeasures: dropped "
-			       "TX packet to " MAC_FMT "\n",
-			       ieee->dev->name, MAC_ARG(header->addr1));
-		}
-		return -1;
-	}
-#endif
 	/* To encrypt, frame format is:
 	 * IV (4 bytes), clear payload (including SNAP), ICV (4 bytes) */
 
diff --git a/net/ieee80211/ieee80211_wx.c b/net/ieee80211/ieee80211_wx.c
index 04f0897..fc4e137 100644
--- a/net/ieee80211/ieee80211_wx.c
+++ b/net/ieee80211/ieee80211_wx.c
@@ -182,7 +182,7 @@
 	if (iwe.u.data.length)
 		start = iwe_stream_add_point(start, stop, &iwe, custom);
 
-	if (ieee->wpa_enabled && network->wpa_ie_len) {
+	if (network->wpa_ie_len) {
 		char buf[MAX_WPA_IE_LEN * 2 + 30];
 
 		u8 *p = buf;
@@ -197,7 +197,7 @@
 		start = iwe_stream_add_point(start, stop, &iwe, buf);
 	}
 
-	if (ieee->wpa_enabled && network->rsn_ie_len) {
+	if (network->rsn_ie_len) {
 		char buf[MAX_WPA_IE_LEN * 2 + 30];
 
 		u8 *p = buf;
@@ -351,7 +351,7 @@
 		}
 
 		if (new_crypt->ops && try_module_get(new_crypt->ops->owner))
-			new_crypt->priv = new_crypt->ops->init(key);
+			new_crypt->priv = new_crypt->ops->init(ieee, key);
 
 		if (!new_crypt->ops || !new_crypt->priv) {
 			kfree(new_crypt);