aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorZhu Yi <yi.zhu@intel.com>2009-10-23 16:42:25 -0400
committerJohn W. Linville <linville@tuxdriver.com>2009-10-27 16:50:01 -0400
commit29b1b2688fd71346f78f175d9669c006686b6dc3 (patch)
tree8d7a337761cc88dae60f4d5e44eb5fd9fbe53858 /drivers
parent52aa081c40324ecb04a47864e4e56dafc5a72a34 (diff)
iwlwifi: fix use after free bug for paged rx
In the paged rx patch (4854fde2), I introduced a bug that could possibly touch an already freed page. It is fixed by avoiding the access in this patch. I've also added some comments so that other people touching the code won't make the same mistake. In the future, if we cannot avoid access the page after being handled to the upper layer, we can use get_page/put_page to handle it. For now, it's just not necessary. It also fixed a debug message print bug reported by Stanislaw Gruszka <sgruszka@redhat.com>. Signed-off-by: Zhu Yi <yi.zhu@intel.com> Signed-off-by: Reinette Chatre <reinette.chatre@intel.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-3945.c15
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-agn.c11
-rw-r--r--drivers/net/wireless/iwlwifi/iwl-rx.c21
-rw-r--r--drivers/net/wireless/iwlwifi/iwl3945-base.c18
4 files changed, 46 insertions, 19 deletions
diff --git a/drivers/net/wireless/iwlwifi/iwl-3945.c b/drivers/net/wireless/iwlwifi/iwl-3945.c
index 269b9889e39e..f5d75288bd27 100644
--- a/drivers/net/wireless/iwlwifi/iwl-3945.c
+++ b/drivers/net/wireless/iwlwifi/iwl-3945.c
@@ -548,6 +548,7 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
548 u16 len = le16_to_cpu(rx_hdr->len); 548 u16 len = le16_to_cpu(rx_hdr->len);
549 struct sk_buff *skb; 549 struct sk_buff *skb;
550 int ret; 550 int ret;
551 __le16 fc = hdr->frame_control;
551 552
552 /* We received data from the HW, so stop the watchdog */ 553 /* We received data from the HW, so stop the watchdog */
553 if (unlikely(len + IWL39_RX_FRAME_SIZE > 554 if (unlikely(len + IWL39_RX_FRAME_SIZE >
@@ -580,9 +581,9 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
580 /* mac80211 currently doesn't support paged SKB. Convert it to 581 /* mac80211 currently doesn't support paged SKB. Convert it to
581 * linear SKB for management frame and data frame requires 582 * linear SKB for management frame and data frame requires
582 * software decryption or software defragementation. */ 583 * software decryption or software defragementation. */
583 if (ieee80211_is_mgmt(hdr->frame_control) || 584 if (ieee80211_is_mgmt(fc) ||
584 ieee80211_has_protected(hdr->frame_control) || 585 ieee80211_has_protected(fc) ||
585 ieee80211_has_morefrags(hdr->frame_control) || 586 ieee80211_has_morefrags(fc) ||
586 le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) 587 le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
587 ret = skb_linearize(skb); 588 ret = skb_linearize(skb);
588 else 589 else
@@ -594,11 +595,15 @@ static void iwl3945_pass_packet_to_mac80211(struct iwl_priv *priv,
594 goto out; 595 goto out;
595 } 596 }
596 597
597 iwl_update_stats(priv, false, hdr->frame_control, len); 598 /*
599 * XXX: We cannot touch the page and its virtual memory (pkt) after
600 * here. It might have already been freed by the above skb change.
601 */
598 602
603 iwl_update_stats(priv, false, fc, len);
599 memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); 604 memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
600 ieee80211_rx(priv->hw, skb);
601 605
606 ieee80211_rx(priv->hw, skb);
602 out: 607 out:
603 priv->alloc_rxb_page--; 608 priv->alloc_rxb_page--;
604 rxb->page = NULL; 609 rxb->page = NULL;
diff --git a/drivers/net/wireless/iwlwifi/iwl-agn.c b/drivers/net/wireless/iwlwifi/iwl-agn.c
index a34acb7e44c3..0d3886505205 100644
--- a/drivers/net/wireless/iwlwifi/iwl-agn.c
+++ b/drivers/net/wireless/iwlwifi/iwl-agn.c
@@ -814,8 +814,8 @@ void iwl_rx_handle(struct iwl_priv *priv)
814 if (priv->rx_handlers[pkt->hdr.cmd]) { 814 if (priv->rx_handlers[pkt->hdr.cmd]) {
815 IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, 815 IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r,
816 i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); 816 i, get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
817 priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
818 priv->isr_stats.rx_handlers[pkt->hdr.cmd]++; 817 priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
818 priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
819 } else { 819 } else {
820 /* No handling needed */ 820 /* No handling needed */
821 IWL_DEBUG_RX(priv, 821 IWL_DEBUG_RX(priv,
@@ -824,11 +824,18 @@ void iwl_rx_handle(struct iwl_priv *priv)
824 pkt->hdr.cmd); 824 pkt->hdr.cmd);
825 } 825 }
826 826
827 /*
828 * XXX: After here, we should always check rxb->page
829 * against NULL before touching it or its virtual
830 * memory (pkt). Because some rx_handler might have
831 * already taken or freed the pages.
832 */
833
827 if (reclaim) { 834 if (reclaim) {
828 /* Invoke any callbacks, transfer the buffer to caller, 835 /* Invoke any callbacks, transfer the buffer to caller,
829 * and fire off the (possibly) blocking iwl_send_cmd() 836 * and fire off the (possibly) blocking iwl_send_cmd()
830 * as we reclaim the driver command queue */ 837 * as we reclaim the driver command queue */
831 if (rxb && rxb->page) 838 if (rxb->page)
832 iwl_tx_cmd_complete(priv, rxb); 839 iwl_tx_cmd_complete(priv, rxb);
833 else 840 else
834 IWL_WARN(priv, "Claim null rxb?\n"); 841 IWL_WARN(priv, "Claim null rxb?\n");
diff --git a/drivers/net/wireless/iwlwifi/iwl-rx.c b/drivers/net/wireless/iwlwifi/iwl-rx.c
index cfc31ae4712b..e5339c9ad13e 100644
--- a/drivers/net/wireless/iwlwifi/iwl-rx.c
+++ b/drivers/net/wireless/iwlwifi/iwl-rx.c
@@ -241,6 +241,7 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
241 struct iwl_rx_mem_buffer *rxb; 241 struct iwl_rx_mem_buffer *rxb;
242 struct page *page; 242 struct page *page;
243 unsigned long flags; 243 unsigned long flags;
244 gfp_t gfp_mask = priority;
244 245
245 while (1) { 246 while (1) {
246 spin_lock_irqsave(&rxq->lock, flags); 247 spin_lock_irqsave(&rxq->lock, flags);
@@ -251,13 +252,13 @@ void iwl_rx_allocate(struct iwl_priv *priv, gfp_t priority)
251 spin_unlock_irqrestore(&rxq->lock, flags); 252 spin_unlock_irqrestore(&rxq->lock, flags);
252 253
253 if (rxq->free_count > RX_LOW_WATERMARK) 254 if (rxq->free_count > RX_LOW_WATERMARK)
254 priority |= __GFP_NOWARN; 255 gfp_mask |= __GFP_NOWARN;
255 256
256 if (priv->hw_params.rx_page_order > 0) 257 if (priv->hw_params.rx_page_order > 0)
257 priority |= __GFP_COMP; 258 gfp_mask |= __GFP_COMP;
258 259
259 /* Alloc a new receive buffer */ 260 /* Alloc a new receive buffer */
260 page = alloc_pages(priority, priv->hw_params.rx_page_order); 261 page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
261 if (!page) { 262 if (!page) {
262 if (net_ratelimit()) 263 if (net_ratelimit())
263 IWL_DEBUG_INFO(priv, "alloc_pages failed, " 264 IWL_DEBUG_INFO(priv, "alloc_pages failed, "
@@ -922,6 +923,7 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
922{ 923{
923 struct sk_buff *skb; 924 struct sk_buff *skb;
924 int ret = 0; 925 int ret = 0;
926 __le16 fc = hdr->frame_control;
925 927
926 /* We only process data packets if the interface is open */ 928 /* We only process data packets if the interface is open */
927 if (unlikely(!priv->is_open)) { 929 if (unlikely(!priv->is_open)) {
@@ -946,9 +948,9 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
946 /* mac80211 currently doesn't support paged SKB. Convert it to 948 /* mac80211 currently doesn't support paged SKB. Convert it to
947 * linear SKB for management frame and data frame requires 949 * linear SKB for management frame and data frame requires
948 * software decryption or software defragementation. */ 950 * software decryption or software defragementation. */
949 if (ieee80211_is_mgmt(hdr->frame_control) || 951 if (ieee80211_is_mgmt(fc) ||
950 ieee80211_has_protected(hdr->frame_control) || 952 ieee80211_has_protected(fc) ||
951 ieee80211_has_morefrags(hdr->frame_control) || 953 ieee80211_has_morefrags(fc) ||
952 le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG) 954 le16_to_cpu(hdr->seq_ctrl) & IEEE80211_SCTL_FRAG)
953 ret = skb_linearize(skb); 955 ret = skb_linearize(skb);
954 else 956 else
@@ -960,7 +962,12 @@ static void iwl_pass_packet_to_mac80211(struct iwl_priv *priv,
960 goto out; 962 goto out;
961 } 963 }
962 964
963 iwl_update_stats(priv, false, hdr->frame_control, len); 965 /*
966 * XXX: We cannot touch the page and its virtual memory (hdr) after
967 * here. It might have already been freed by the above skb change.
968 */
969
970 iwl_update_stats(priv, false, fc, len);
964 memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats)); 971 memcpy(IEEE80211_SKB_RXCB(skb), stats, sizeof(*stats));
965 972
966 ieee80211_rx(priv->hw, skb); 973 ieee80211_rx(priv->hw, skb);
diff --git a/drivers/net/wireless/iwlwifi/iwl3945-base.c b/drivers/net/wireless/iwlwifi/iwl3945-base.c
index 5977a57a234c..8b08bdc10bc9 100644
--- a/drivers/net/wireless/iwlwifi/iwl3945-base.c
+++ b/drivers/net/wireless/iwlwifi/iwl3945-base.c
@@ -1124,6 +1124,7 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
1124 struct iwl_rx_mem_buffer *rxb; 1124 struct iwl_rx_mem_buffer *rxb;
1125 struct page *page; 1125 struct page *page;
1126 unsigned long flags; 1126 unsigned long flags;
1127 gfp_t gfp_mask = priority;
1127 1128
1128 while (1) { 1129 while (1) {
1129 spin_lock_irqsave(&rxq->lock, flags); 1130 spin_lock_irqsave(&rxq->lock, flags);
@@ -1135,13 +1136,13 @@ static void iwl3945_rx_allocate(struct iwl_priv *priv, gfp_t priority)
1135 spin_unlock_irqrestore(&rxq->lock, flags); 1136 spin_unlock_irqrestore(&rxq->lock, flags);
1136 1137
1137 if (rxq->free_count > RX_LOW_WATERMARK) 1138 if (rxq->free_count > RX_LOW_WATERMARK)
1138 priority |= __GFP_NOWARN; 1139 gfp_mask |= __GFP_NOWARN;
1139 1140
1140 if (priv->hw_params.rx_page_order > 0) 1141 if (priv->hw_params.rx_page_order > 0)
1141 priority |= __GFP_COMP; 1142 gfp_mask |= __GFP_COMP;
1142 1143
1143 /* Alloc a new receive buffer */ 1144 /* Alloc a new receive buffer */
1144 page = alloc_pages(priority, priv->hw_params.rx_page_order); 1145 page = alloc_pages(gfp_mask, priv->hw_params.rx_page_order);
1145 if (!page) { 1146 if (!page) {
1146 if (net_ratelimit()) 1147 if (net_ratelimit())
1147 IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n"); 1148 IWL_DEBUG_INFO(priv, "Failed to allocate SKB buffer.\n");
@@ -1410,8 +1411,8 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
1410 if (priv->rx_handlers[pkt->hdr.cmd]) { 1411 if (priv->rx_handlers[pkt->hdr.cmd]) {
1411 IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i, 1412 IWL_DEBUG_RX(priv, "r = %d, i = %d, %s, 0x%02x\n", r, i,
1412 get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd); 1413 get_cmd_string(pkt->hdr.cmd), pkt->hdr.cmd);
1413 priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
1414 priv->isr_stats.rx_handlers[pkt->hdr.cmd]++; 1414 priv->isr_stats.rx_handlers[pkt->hdr.cmd]++;
1415 priv->rx_handlers[pkt->hdr.cmd] (priv, rxb);
1415 } else { 1416 } else {
1416 /* No handling needed */ 1417 /* No handling needed */
1417 IWL_DEBUG_RX(priv, 1418 IWL_DEBUG_RX(priv,
@@ -1420,11 +1421,18 @@ static void iwl3945_rx_handle(struct iwl_priv *priv)
1420 pkt->hdr.cmd); 1421 pkt->hdr.cmd);
1421 } 1422 }
1422 1423
1424 /*
1425 * XXX: After here, we should always check rxb->page
1426 * against NULL before touching it or its virtual
1427 * memory (pkt). Because some rx_handler might have
1428 * already taken or freed the pages.
1429 */
1430
1423 if (reclaim) { 1431 if (reclaim) {
1424 /* Invoke any callbacks, transfer the buffer to caller, 1432 /* Invoke any callbacks, transfer the buffer to caller,
1425 * and fire off the (possibly) blocking iwl_send_cmd() 1433 * and fire off the (possibly) blocking iwl_send_cmd()
1426 * as we reclaim the driver command queue */ 1434 * as we reclaim the driver command queue */
1427 if (rxb && rxb->page) 1435 if (rxb->page)
1428 iwl_tx_cmd_complete(priv, rxb); 1436 iwl_tx_cmd_complete(priv, rxb);
1429 else 1437 else
1430 IWL_WARN(priv, "Claim null rxb?\n"); 1438 IWL_WARN(priv, "Claim null rxb?\n");