ath10k: prevent using invalid ringbuffer indexes
authorMichal Kazior <michal.kazior@tieto.com>
Wed, 31 Jul 2013 08:55:11 +0000 (10:55 +0200)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 2 Aug 2013 06:37:28 +0000 (09:37 +0300)
If the device is removed and hotplug fails
ioread32() will return 0xFFFFFFFF. In that case
reading ringbuffer during device bringup led to
out-of-bounds addressing of a ringbuffer array
that in turn led to a paging failure.

This could be reproduced by the following:
 * boot without acpi/prevent hotplug from working
 * insert and manually detect (pci rescan) the device
 * remove the device physically
 * load ath10k driver
 * kernel crashed

Ringbuffer index reading is now protected by using
an appropriate mask to prevent addressing an
invalid array index.

Signed-off-by: Michal Kazior <michal.kazior@tieto.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/ce.c

index b407929..f8b969f 100644 (file)
@@ -637,6 +637,7 @@ static int ath10k_ce_completed_send_next_nolock(struct ce_state *ce_state,
                ath10k_pci_wake(ar);
                src_ring->hw_index =
                        ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
                ath10k_pci_wake(ar);
                src_ring->hw_index =
                        ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+               src_ring->hw_index &= nentries_mask;
                ath10k_pci_sleep(ar);
        }
        read_index = src_ring->hw_index;
                ath10k_pci_sleep(ar);
        }
        read_index = src_ring->hw_index;
@@ -950,10 +951,12 @@ static int ath10k_ce_init_src_ring(struct ath10k *ar,
 
        ath10k_pci_wake(ar);
        src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
 
        ath10k_pci_wake(ar);
        src_ring->sw_index = ath10k_ce_src_ring_read_index_get(ar, ctrl_addr);
+       src_ring->sw_index &= src_ring->nentries_mask;
        src_ring->hw_index = src_ring->sw_index;
 
        src_ring->write_index =
                ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
        src_ring->hw_index = src_ring->sw_index;
 
        src_ring->write_index =
                ath10k_ce_src_ring_write_index_get(ar, ctrl_addr);
+       src_ring->write_index &= src_ring->nentries_mask;
        ath10k_pci_sleep(ar);
 
        src_ring->per_transfer_context = (void **)ptr;
        ath10k_pci_sleep(ar);
 
        src_ring->per_transfer_context = (void **)ptr;
@@ -1035,8 +1038,10 @@ static int ath10k_ce_init_dest_ring(struct ath10k *ar,
 
        ath10k_pci_wake(ar);
        dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
 
        ath10k_pci_wake(ar);
        dest_ring->sw_index = ath10k_ce_dest_ring_read_index_get(ar, ctrl_addr);
+       dest_ring->sw_index &= dest_ring->nentries_mask;
        dest_ring->write_index =
                ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
        dest_ring->write_index =
                ath10k_ce_dest_ring_write_index_get(ar, ctrl_addr);
+       dest_ring->write_index &= dest_ring->nentries_mask;
        ath10k_pci_sleep(ar);
 
        dest_ring->per_transfer_context = (void **)ptr;
        ath10k_pci_sleep(ar);
 
        dest_ring->per_transfer_context = (void **)ptr;