Browse Source

qcacmn: Cleanup defrag waitlist when removing peer

Peer's RX TID may already be added to defrag's waitlist when it is
removed. Then use-after-free occurs when waitlist be accessed later.
Remove current peer's entries from waitlist, before it is freed.

As waitlist may be accessed from two contexts, additional lock
introduced to protect waitlist.

CRs-Fixed: 2244566
Change-Id: I8f1935973372a81086e9dbd992ac771b6709a677
Lin Bai 6 years ago
parent
commit
f1c577ee03
4 changed files with 33 additions and 6 deletions
  1. 3 0
      dp/wifi3.0/dp_main.c
  2. 6 0
      dp/wifi3.0/dp_peer.c
  3. 23 6
      dp/wifi3.0/dp_rx_defrag.c
  4. 1 0
      dp/wifi3.0/dp_types.h

+ 3 - 0
dp/wifi3.0/dp_main.c

@@ -2253,6 +2253,7 @@ static int dp_soc_cmn_setup(struct dp_soc *soc)
 		wlan_cfg_get_rx_defrag_min_timeout(soc->wlan_cfg_ctx);
 	soc->rx.flags.defrag_timeout_check =
 		wlan_cfg_get_defrag_timeout_check(soc->wlan_cfg_ctx);
+	qdf_spinlock_create(&soc->rx.defrag.defrag_lock);
 
 out:
 	/*
@@ -3048,6 +3049,8 @@ static void dp_soc_detach_wifi3(void *txrx_soc)
 
 	htt_soc_detach(soc->htt_handle);
 
+	qdf_spinlock_destroy(&soc->rx.defrag.defrag_lock);
+
 	dp_reo_cmdlist_destroy(soc);
 	qdf_spinlock_destroy(&soc->rx.reo_cmd_lock);
 	dp_reo_desc_freelist_destroy(soc);

+ 6 - 0
dp/wifi3.0/dp_peer.c

@@ -22,6 +22,7 @@
 #include "dp_types.h"
 #include "dp_internal.h"
 #include "dp_peer.h"
+#include "dp_rx_defrag.h"
 #include <hal_api.h>
 #include <hal_reo.h>
 #ifdef CONFIG_MCL
@@ -1570,6 +1571,11 @@ void dp_peer_rx_cleanup(struct dp_vdev *vdev, struct dp_peer *peer)
 	for (tid = 0; tid < DP_MAX_TIDS; tid++) {
 		if (peer->rx_tid[tid].hw_qdesc_vaddr_unaligned != NULL) {
 			dp_rx_tid_delete_wifi3(peer, tid);
+
+			/* Cleanup defrag related resource */
+			dp_rx_defrag_waitlist_remove(peer, tid);
+			dp_rx_reorder_flush_frag(peer, tid);
+
 			tid_delete_mask |= (1 << tid);
 		}
 	}

+ 23 - 6
dp/wifi3.0/dp_rx_defrag.c

@@ -150,10 +150,13 @@ void dp_rx_defrag_waitlist_flush(struct dp_soc *soc)
 	struct dp_rx_tid *rx_reorder;
 	struct dp_rx_tid *tmp;
 	uint32_t now_ms = qdf_system_ticks_to_msecs(qdf_system_ticks());
+	TAILQ_HEAD(, dp_rx_tid) temp_list;
 
+	TAILQ_INIT(&temp_list);
+
+	qdf_spin_lock_bh(&soc->rx.defrag.defrag_lock);
 	TAILQ_FOREACH_SAFE(rx_reorder, &soc->rx.defrag.waitlist,
 			   defrag_waitlist_elem, tmp) {
-		struct dp_peer *peer;
 		unsigned int tid;
 
 		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
@@ -170,14 +173,24 @@ void dp_rx_defrag_waitlist_flush(struct dp_soc *soc)
 			continue;
 		}
 
+		TAILQ_REMOVE(&soc->rx.defrag.waitlist, rx_reorder,
+			     defrag_waitlist_elem);
+
+		/* Move to temp list and clean-up later */
+		TAILQ_INSERT_TAIL(&temp_list, rx_reorder,
+				  defrag_waitlist_elem);
+	}
+	qdf_spin_unlock_bh(&soc->rx.defrag.defrag_lock);
+
+	TAILQ_FOREACH_SAFE(rx_reorder, &temp_list,
+			   defrag_waitlist_elem, tmp) {
+		struct dp_peer *peer;
+
 		/* get address of current peer */
 		peer =
 			container_of(rx_reorder, struct dp_peer,
-				     rx_tid[tid]);
-
-		TAILQ_REMOVE(&soc->rx.defrag.waitlist, rx_reorder,
-			     defrag_waitlist_elem);
-		dp_rx_reorder_flush_frag(peer, tid);
+				     rx_tid[rx_reorder->tid]);
+		dp_rx_reorder_flush_frag(peer, rx_reorder->tid);
 	}
 }
 
@@ -200,8 +213,10 @@ static void dp_rx_defrag_waitlist_add(struct dp_peer *peer, unsigned tid)
 				tid, peer);
 
 	/* TODO: use LIST macros instead of TAIL macros */
+	qdf_spin_lock_bh(&psoc->rx.defrag.defrag_lock);
 	TAILQ_INSERT_TAIL(&psoc->rx.defrag.waitlist, rx_reorder,
 				defrag_waitlist_elem);
+	qdf_spin_unlock_bh(&psoc->rx.defrag.defrag_lock);
 }
 
 /*
@@ -230,6 +245,7 @@ void dp_rx_defrag_waitlist_remove(struct dp_peer *peer, unsigned tid)
 				FL("Remove TID %u from waitlist for peer %pK"),
 				tid, peer);
 
+	qdf_spin_lock_bh(&soc->rx.defrag.defrag_lock);
 	TAILQ_FOREACH(rx_reorder, &soc->rx.defrag.waitlist,
 			   defrag_waitlist_elem) {
 		struct dp_peer *peer_on_waitlist;
@@ -244,6 +260,7 @@ void dp_rx_defrag_waitlist_remove(struct dp_peer *peer, unsigned tid)
 			TAILQ_REMOVE(&soc->rx.defrag.waitlist,
 				rx_reorder, defrag_waitlist_elem);
 	}
+	qdf_spin_unlock_bh(&soc->rx.defrag.defrag_lock);
 }
 
 /*

+ 1 - 0
dp/wifi3.0/dp_types.h

@@ -755,6 +755,7 @@ struct dp_soc {
 		struct {
 			TAILQ_HEAD(, dp_rx_tid) waitlist;
 			uint32_t timeout_ms;
+			qdf_spinlock_t defrag_lock;
 		} defrag;
 		struct {
 			int defrag_timeout_check;