Pārlūkot izejas kodu

qcacld-3.0: Fix peer poison overwritten issue

In the existing impementation, once wma_is_pkt_drop_candidate gets a
peer from ol_txrx_find_peer_by_addr, the peer can be deleted in the
SOFTIRQ path from the unmap handler. This would make the peer pointer
'stale' resulting in access to already freed memory.

- Use standard API OL_TXRX_PEER_UNREF_DELETE to decrement peer->ref_cnt
  instead of directly referencing it.
- Add a new API - ol_txrx_find_peer_by_addr_inc_ref which does not
  decrement the peer->ref_cnt until the usage of peer in the caller
  function is finished. The existing API ol_txrx_find_peer_by_addr
  can be replaced by the new API as and when the issues are seen.

Sample usage:
    {
	peer = ol_txrx_find_peer_by_addr_inc_ref
	/* This API gets the peer and increments its ref_cnt */
	...
	...
	/* Once peer usage is done */
	OL_TXRX_PEER_UNREF_DELETE(peer);
	/*
	 * This API deletes the reference to the peer or the peer itself
	 * if the peer->ref_cnt is 0. This way we no longer depend on
	 * peer unmaps to delete the peer.
	 */

    }

Change-Id: I69fb67a4b4c9e26344d2ed1a72c383be7ac62414
CRs-Fixed: 2008583
Mohit Khanna 8 gadi atpakaļ
vecāks
revīzija
babadb8bee

+ 7 - 5
core/dp/txrx/ol_tx_classify.c

@@ -464,7 +464,7 @@ ol_tx_classify(
 			 * classify_extension function can check whether to
 			 * encrypt multicast / broadcast frames.
 			 */
-			peer = ol_txrx_peer_find_hash_find(pdev,
+			peer = ol_txrx_peer_find_hash_find_inc_ref(pdev,
 							vdev->mac_addr.raw,
 							0, 1);
 			if (!peer) {
@@ -523,8 +523,9 @@ ol_tx_classify(
 			 */
 			peer = ol_tx_tdls_peer_find(pdev, vdev, &peer_id);
 		} else {
-			peer = ol_txrx_peer_find_hash_find(pdev, dest_addr,
-									0, 1);
+			peer = ol_txrx_peer_find_hash_find_inc_ref(pdev,
+								dest_addr,
+								0, 1);
 		}
 		tx_msdu_info->htt.info.is_unicast = true;
 		if (!peer) {
@@ -694,8 +695,9 @@ ol_tx_classify_mgmt(
 			}
 		} else {
 			/* find the peer and increment its reference count */
-			peer = ol_txrx_peer_find_hash_find(pdev, dest_addr,
-							   0, 1);
+			peer = ol_txrx_peer_find_hash_find_inc_ref(pdev,
+								dest_addr,
+								0, 1);
 		}
 		tx_msdu_info->peer = peer;
 		if (!peer) {

+ 63 - 9
core/dp/txrx/ol_txrx.c

@@ -310,7 +310,7 @@ ol_txrx_find_peer_by_addr_and_vdev(struct cdp_pdev *ppdev,
 	if (!peer)
 		return NULL;
 	*peer_id = peer->local_id;
-	OL_TXRX_PEER_DEC_REF_CNT(peer);
+	OL_TXRX_PEER_UNREF_DELETE(peer);
 	return peer;
 }
 
@@ -355,6 +355,19 @@ static struct cdp_vdev *ol_txrx_get_vdev_by_sta_id(uint8_t sta_id)
 	return (struct cdp_vdev *)peer->vdev;
 }
 
+/**
+ * ol_txrx_find_peer_by_addr() - find peer via peer mac addr and peer_id
+ * @ppdev: pointer of type cdp_pdev
+ * @peer_addr: peer mac addr
+ * @peer_id: pointer to fill in the value of peer->local_id for caller
+ *
+ * This function finds a peer with given mac address and returns its peer_id.
+ * Note that this function does not increment the peer->ref_cnt.
+ * This means that the peer may be deleted in some other parallel context after
+ * its been found.
+ *
+ * Return: peer handle if peer is found, NULL if peer is not found.
+ */
 void *ol_txrx_find_peer_by_addr(struct cdp_pdev *ppdev,
 					      uint8_t *peer_addr,
 					      uint8_t *peer_id)
@@ -362,11 +375,48 @@ void *ol_txrx_find_peer_by_addr(struct cdp_pdev *ppdev,
 	struct ol_txrx_peer_t *peer;
 	struct ol_txrx_pdev_t *pdev = (struct ol_txrx_pdev_t *)ppdev;
 
-	peer = ol_txrx_peer_find_hash_find(pdev, peer_addr, 0, 1);
+	peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_addr, 0, 1);
+	if (!peer)
+		return NULL;
+	*peer_id = peer->local_id;
+	OL_TXRX_PEER_UNREF_DELETE(peer);
+	return peer;
+}
+
+/**
+ * ol_txrx_find_peer_by_addr_inc_ref() - find peer via peer mac addr and peer_id
+ * @pdev: pointer of type ol_txrx_pdev_handle
+ * @peer_addr: peer mac addr
+ * @peer_id: pointer to fill in the value of peer->local_id for caller
+ *
+ * This function finds the peer with given mac address and returns its peer_id.
+ * Note that this function increments the peer->ref_cnt.
+ * This makes sure that peer will be valid. This also means the caller needs to
+ * call the corresponding API - OL_TXRX_PEER_UNREF_DELETE to delete the peer
+ * reference.
+ * Sample usage:
+ *    {
+ *      //the API call below increments the peer->ref_cnt
+ *      peer = ol_txrx_find_peer_by_addr_inc_ref(pdev, peer_addr, peer_id);
+ *
+ *      // Once peer usage is done
+ *
+ *      //the API call below decrements the peer->ref_cnt
+ *       OL_TXRX_PEER_UNREF_DELETE(peer);
+ *    }
+ *
+ * Return: peer handle if the peer is found, NULL if peer is not found.
+ */
+ol_txrx_peer_handle ol_txrx_find_peer_by_addr_inc_ref(ol_txrx_pdev_handle pdev,
+						uint8_t *peer_addr,
+						uint8_t *peer_id)
+{
+	struct ol_txrx_peer_t *peer;
+
+	peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_addr, 0, 1);
 	if (!peer)
 		return NULL;
 	*peer_id = peer->local_id;
-	OL_TXRX_PEER_DEC_REF_CNT(peer);
 	return peer;
 }
 
@@ -2919,7 +2969,7 @@ QDF_STATUS ol_txrx_peer_state_update(struct cdp_pdev *ppdev,
 		return QDF_STATUS_E_INVAL;
 	}
 
-	peer =  ol_txrx_peer_find_hash_find(pdev, peer_mac, 0, 1);
+	peer =  ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_mac, 0, 1);
 	if (NULL == peer) {
 		ol_txrx_err(
 			   "%s: peer is null for peer_mac 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n",
@@ -2996,7 +3046,7 @@ ol_txrx_peer_update(ol_txrx_vdev_handle vdev,
 	struct ol_txrx_peer_t *peer;
 	int    peer_ref_cnt;
 
-	peer = ol_txrx_peer_find_hash_find(vdev->pdev, peer_mac, 0, 1);
+	peer = ol_txrx_peer_find_hash_find_inc_ref(vdev->pdev, peer_mac, 0, 1);
 	if (!peer) {
 		ol_txrx_dbg("%s: peer is null",
 			   __func__);
@@ -3167,6 +3217,11 @@ int ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer,
 	if (qdf_atomic_dec_and_test(&peer->ref_cnt)) {
 		u_int16_t peer_id;
 
+		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO,
+			   "[%s][%d]: Deleting peer %p (%pM) ref_cnt %d\n",
+			   fname, line, peer, peer->mac_addr.raw,
+			   qdf_atomic_read(&peer->ref_cnt));
+
 		peer_id = peer->local_id;
 		/* remove the reference to the peer from the hash table */
 		ol_txrx_peer_find_hash_remove(pdev, peer);
@@ -3271,8 +3326,8 @@ int ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer,
 	} else {
 		qdf_spin_unlock_bh(&pdev->peer_ref_mutex);
 		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
-			  "ref delete(%s): peer %p peer->ref_cnt = %d",
-			  fname, peer, rc);
+			  "[%s][%d]: ref delete peer %p peer->ref_cnt = %d",
+			  fname, line, peer, rc);
 	}
 
 	return rc;
@@ -3496,8 +3551,7 @@ ol_txrx_peer_handle
 ol_txrx_peer_find_by_addr(struct ol_txrx_pdev_t *pdev, uint8_t *peer_mac_addr)
 {
 	struct ol_txrx_peer_t *peer;
-
-	peer = ol_txrx_peer_find_hash_find(pdev, peer_mac_addr, 0, 0);
+	peer = ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_mac_addr, 0, 0);
 	if (peer) {
 		ol_txrx_info_high(
 			   "%s: Delete extra reference %p", __func__, peer);

+ 3 - 1
core/dp/txrx/ol_txrx.h

@@ -46,7 +46,9 @@ int ol_txrx_peer_unref_delete(ol_txrx_peer_handle peer,
 					      const char *fname,
 					      int line);
 
-
+ol_txrx_peer_handle ol_txrx_find_peer_by_addr_inc_ref(ol_txrx_pdev_handle pdev,
+						uint8_t *peer_addr,
+						uint8_t *peer_id);
 /**
  * ol_tx_desc_pool_size_hl() - allocate tx descriptor pool size for HL systems
  * @ctrl_pdev: the control pdev handle

+ 11 - 9
core/dp/txrx/ol_txrx_peer_find.c

@@ -85,7 +85,7 @@ void __ol_txrx_peer_change_ref_cnt(struct ol_txrx_peer_t *peer,
 {
 	qdf_atomic_add(change, &peer->ref_cnt);
 	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_INFO_HIGH,
-		"[%s][%d]: peer %p peer->ref_cnt changed by(%d) to %d",
+		"[%s][%d]: peer %p peer->ref_cnt changed by (%d) to %d",
 		fname, line, peer, change, qdf_atomic_read(&peer->ref_cnt));
 }
 
@@ -209,10 +209,12 @@ struct ol_txrx_peer_t *ol_txrx_peer_vdev_find_hash(struct ol_txrx_pdev_t *pdev,
 	return NULL;            /* failure */
 }
 
-struct ol_txrx_peer_t *ol_txrx_peer_find_hash_find(struct ol_txrx_pdev_t *pdev,
-						   uint8_t *peer_mac_addr,
-						   int mac_addr_is_aligned,
-						   uint8_t check_valid)
+struct ol_txrx_peer_t *
+ol_txrx_peer_find_hash_find_inc_ref
+				(struct ol_txrx_pdev_t *pdev,
+				uint8_t *peer_mac_addr,
+				int mac_addr_is_aligned,
+				uint8_t check_valid)
 {
 	union ol_txrx_align_mac_addr_t local_mac_addr_aligned, *mac_addr;
 	unsigned int index;
@@ -352,7 +354,7 @@ static inline void ol_txrx_peer_find_add_id(struct ol_txrx_pdev_t *pdev,
 
 	/* check if there's already a peer object with this MAC address */
 	peer =
-		ol_txrx_peer_find_hash_find(pdev, peer_mac_addr,
+		ol_txrx_peer_find_hash_find_inc_ref(pdev, peer_mac_addr,
 					    1 /* is aligned */, 0);
 
 	if (!peer || peer_id == HTT_INVALID_PEER) {
@@ -555,7 +557,7 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
 							del_peer_id_ref_cnt);
 		qdf_spin_unlock_bh(&pdev->peer_map_unmap_lock);
 		ol_txrx_dbg(
-			   "%s: Remove the ID %d reference to deleted peer. del_peer_id_ref_cnt %d",
+			   "%s: peer already deleted, peer_id %d del_peer_id_ref_cnt %d",
 			   __func__, peer_id, ref_cnt);
 		return;
 	}
@@ -597,8 +599,8 @@ void ol_rx_peer_unmap_handler(ol_txrx_pdev_handle pdev, uint16_t peer_id)
 	OL_TXRX_PEER_UNREF_DELETE(peer);
 
 	QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_DEBUG,
-	   "%s: Remove the ID %d reference to peer %p peer_id_ref_cnt %d",
-	   __func__, peer_id, peer, ref_cnt);
+		  "%s: peer_id %d peer %p peer_id_ref_cnt %d",
+		  __func__, peer_id, peer, ref_cnt);
 }
 
 /**

+ 6 - 7
core/dp/txrx/ol_txrx_peer_find.h

@@ -40,9 +40,6 @@
 #define OL_TXRX_PEER_INC_REF_CNT(peer) \
 	__ol_txrx_peer_change_ref_cnt(peer, 1, __func__, __LINE__);
 
-#define OL_TXRX_PEER_DEC_REF_CNT(peer) \
-	__ol_txrx_peer_change_ref_cnt(peer, (-1), __func__, __LINE__);
-
 void __ol_txrx_peer_change_ref_cnt(struct ol_txrx_peer_t *peer,
 						int change,
 						const char *fname,
@@ -99,10 +96,12 @@ void
 ol_txrx_peer_find_hash_add(struct ol_txrx_pdev_t *pdev,
 			   struct ol_txrx_peer_t *peer);
 
-struct ol_txrx_peer_t *ol_txrx_peer_find_hash_find(struct ol_txrx_pdev_t *pdev,
-						   uint8_t *peer_mac_addr,
-						   int mac_addr_is_aligned,
-						   uint8_t check_valid);
+struct ol_txrx_peer_t *
+ol_txrx_peer_find_hash_find_inc_ref
+				(struct ol_txrx_pdev_t *pdev,
+				uint8_t *peer_mac_addr,
+				int mac_addr_is_aligned,
+				uint8_t check_valid);
 
 struct
 ol_txrx_peer_t *ol_txrx_peer_vdev_find_hash(struct ol_txrx_pdev_t *pdev,