]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ath10k: fix handling of wierd MSDU chaining cases
authorMichal Kazior <michal.kazior@tieto.com>
Wed, 14 May 2014 13:23:31 +0000 (16:23 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Wed, 14 May 2014 13:41:16 +0000 (16:41 +0300)
Apparently firmware can sometimes report a
sequence with the first rx descriptor saying it's
not the last MSDU. In that case msdu_chaining
value could be overwritten saying it's not a
chained MSDU. This in turn led to skb_push panic
as the frame could be treated as an A-MSDU instead
of a chained MSDU.

Reported-By: Avery Pennarun <apenwarr@gmail.com>
Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/htt_rx.c

index db6c8af0c9b182f9d960f7b5b81d6325735c65b4..ac6a5fe75c87b764226b8b6b605c28fef7877c38 100644 (file)
@@ -312,6 +312,7 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        int msdu_len, msdu_chaining = 0;
        struct sk_buff *msdu;
        struct htt_rx_desc *rx_desc;
+       bool corrupted = false;
 
        lockdep_assert_held(&htt->rx_ring.lock);
 
@@ -405,7 +406,6 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
                msdu_len = MS(__le32_to_cpu(rx_desc->msdu_start.info0),
                              RX_MSDU_START_INFO0_MSDU_LENGTH);
                msdu_chained = rx_desc->frag_info.ring2_more_count;
-               msdu_chaining = msdu_chained;
 
                if (msdu_len_invalid)
                        msdu_len = 0;
@@ -433,11 +433,15 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
 
                        msdu->next = next;
                        msdu = next;
+                       msdu_chaining = 1;
                }
 
                last_msdu = __le32_to_cpu(rx_desc->msdu_end.info0) &
                                RX_MSDU_END_INFO0_LAST_MSDU;
 
+               if (msdu_chaining && !last_msdu)
+                       corrupted = true;
+
                if (last_msdu) {
                        msdu->next = NULL;
                        break;
@@ -452,6 +456,20 @@ static int ath10k_htt_rx_amsdu_pop(struct ath10k_htt *htt,
        if (*head_msdu == NULL)
                msdu_chaining = -1;
 
+       /*
+        * Apparently FW sometimes reports weird chained MSDU sequences with
+        * more than one rx descriptor. This seems like a bug but needs more
+        * analyzing. For the time being fix it by dropping such sequences to
+        * avoid blowing up the host system.
+        */
+       if (corrupted) {
+               ath10k_warn("failed to pop chained msdus, dropping\n");
+               ath10k_htt_rx_free_msdu_chain(*head_msdu);
+               *head_msdu = NULL;
+               *tail_msdu = NULL;
+               msdu_chaining = -EINVAL;
+       }
+
        /*
         * Don't refill the ring yet.
         *