ath10k: ensure peer_map references are cleaned up
authorBen Greear <greearb@candelatech.com>
Thu, 30 Jun 2016 12:23:53 +0000 (15:23 +0300)
committerKalle Valo <kvalo@qca.qualcomm.com>
Fri, 8 Jul 2016 06:41:59 +0000 (09:41 +0300)
While debugging OS crashes due to firmware crashes, I enabled
kasan, and it noticed that peer objects were being used-after-freed.

Looks like there are two places we could be leaving stale references
in the peer-map, so clean that up.

Signed-off-by: Ben Greear <greearb@candelatech.com>
Signed-off-by: Kalle Valo <kvalo@qca.qualcomm.com>
drivers/net/wireless/ath/ath10k/mac.c

index c6c4ae708c156c2ec9034501e371b304ae716cbf..78add06fd97089d75e258d9ac01d8f0ad9d6e56e 100644 (file)
@@ -802,6 +802,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 {
        struct ath10k_peer *peer, *tmp;
        int peer_id;
+       int i;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -818,6 +819,17 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
                        ar->peer_map[peer_id] = NULL;
                }
 
+               /* Double check that peer is properly un-referenced from
+                * the peer_map
+                */
+               for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++) {
+                       if (ar->peer_map[i] == peer) {
+                               ath10k_warn(ar, "removing stale peer_map entry for %pM (ptr %p idx %d)\n",
+                                           peer->addr, peer, i);
+                               ar->peer_map[i] = NULL;
+                       }
+               }
+
                list_del(&peer->list);
                kfree(peer);
                ar->num_peers--;
@@ -828,6 +840,7 @@ static void ath10k_peer_cleanup(struct ath10k *ar, u32 vdev_id)
 static void ath10k_peer_cleanup_all(struct ath10k *ar)
 {
        struct ath10k_peer *peer, *tmp;
+       int i;
 
        lockdep_assert_held(&ar->conf_mutex);
 
@@ -836,6 +849,10 @@ static void ath10k_peer_cleanup_all(struct ath10k *ar)
                list_del(&peer->list);
                kfree(peer);
        }
+
+       for (i = 0; i < ARRAY_SIZE(ar->peer_map); i++)
+               ar->peer_map[i] = NULL;
+
        spin_unlock_bh(&ar->data_lock);
 
        ar->num_peers = 0;