Browse Source

qcacmn: dp_rx_null_q_desc_handle: drop if msdu_len > RX_BUFFER_SIZE

In some cases, the msdu_len as retrieved fromm msdu_start TLV is
greater than RX_BUFFER_SIZE. In such cases, when qdf_nbuf_set_pktlen
is called, it will expand the skb to accommodate the bigger packet.
This makes the rx_tlv_hdr var invalid, since it continues to point to
the older skb->data. Access of this rx_tlv_hdr will cause exception.

Drop the packet if msdu_len > RX_BUFFER_SIZE. This is not expected while
reaping WBM RX Release ring.

Change-Id: I60890e4d3ee0afa451884d4df458eb50be280280
CRs-Fixed: 2426245
Mohit Khanna 6 years ago
parent
commit
f085b61b59
3 changed files with 81 additions and 89 deletions
  1. 3 1
      dp/wifi3.0/dp_main.c
  2. 76 88
      dp/wifi3.0/dp_rx_err.c
  3. 2 0
      dp/wifi3.0/dp_types.h

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

@@ -8904,12 +8904,14 @@ static void dp_txrx_path_stats(struct dp_soc *soc)
 			       "raw packets %u msdus ( %llu bytes),",
 			       pdev->stats.rx.raw.num,
 			       pdev->stats.rx.raw.bytes);
-		DP_TRACE_STATS(INFO_HIGH, "dropped: error %u msdus",
+		DP_TRACE_STATS(INFO_HIGH, "mic errors %u",
 			       pdev->stats.rx.err.mic_err);
 		DP_TRACE_STATS(INFO_HIGH, "Invalid peer on rx path: %u",
 			       pdev->soc->stats.rx.err.rx_invalid_peer.num);
 		DP_TRACE_STATS(INFO_HIGH, "sw_peer_id invalid %u",
 			       pdev->soc->stats.rx.err.rx_invalid_peer_id.num);
+		DP_TRACE_STATS(INFO_HIGH, "packet_len invalid %u",
+			       pdev->soc->stats.rx.err.rx_invalid_pkt_len.num);
 
 
 		DP_TRACE_STATS(INFO_HIGH, "Reo Statistics");

+ 76 - 88
dp/wifi3.0/dp_rx_err.c

@@ -608,6 +608,27 @@ dp_rx_null_q_handle_invalid_peer_id_exception(struct dp_soc *soc,
 	}
 	return false;
 }
+
+/**
+ * dp_rx_null_q_check_pkt_len_exception() - Check for pktlen validity
+ * @soc: DP SOC context
+ * @pkt_len: computed length of the pkt from caller in bytes
+ *
+ * Return: true if pktlen > RX_BUFFER_SIZE, else return false
+ *
+ */
+static inline
+bool dp_rx_null_q_check_pkt_len_exception(struct dp_soc *soc, uint32_t pkt_len)
+{
+	if (qdf_unlikely(pkt_len > RX_BUFFER_SIZE)) {
+		DP_STATS_INC_PKT(soc, rx.err.rx_invalid_pkt_len,
+				 1, pkt_len);
+		return true;
+	} else {
+		return false;
+	}
+}
+
 #else
 static inline bool
 dp_rx_null_q_handle_invalid_peer_id_exception(struct dp_soc *soc,
@@ -617,6 +638,13 @@ dp_rx_null_q_handle_invalid_peer_id_exception(struct dp_soc *soc,
 {
 	return false;
 }
+
+static inline
+bool dp_rx_null_q_check_pkt_len_exception(struct dp_soc *soc, uint32_t pkt_len)
+{
+	return false;
+}
+
 #endif
 
 /**
@@ -638,8 +666,10 @@ dp_rx_null_q_handle_invalid_peer_id_exception(struct dp_soc *soc,
  * non-QOS TID queue, in the absence of any other default TID queue.
  * This error can show up both in a REO destination or WBM release ring.
  *
+ * Return: QDF_STATUS_SUCCESS, if nbuf handled successfully. QDF status code
+ *         if nbuf could not be handled or dropped.
  */
-static void
+static QDF_STATUS
 dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 			 uint8_t *rx_tlv_hdr, uint8_t pool_id,
 			 struct dp_peer *peer)
@@ -659,23 +689,24 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 	msdu_len = hal_rx_msdu_start_msdu_len_get(rx_tlv_hdr);
 	pkt_len = msdu_len + l2_hdr_offset + RX_PKT_TLVS_LEN;
 
-	/* Set length in nbuf */
-	if (!qdf_nbuf_get_ext_list(nbuf))
-		qdf_nbuf_set_pktlen(nbuf, pkt_len);
 
-	QDF_TRACE_ERROR_RL(QDF_MODULE_ID_DP,
-			   "Len %d Extn list %pK ",
-			   (uint32_t)qdf_nbuf_len(nbuf),
-			   qdf_nbuf_get_ext_list(nbuf));
+	if (!qdf_nbuf_get_ext_list(nbuf)) {
+		if (dp_rx_null_q_check_pkt_len_exception(soc, pkt_len))
+			goto drop_nbuf;
+
+		/* Set length in nbuf */
+		qdf_nbuf_set_pktlen(nbuf,
+				    qdf_min(pkt_len, (uint32_t)RX_BUFFER_SIZE));
+		qdf_assert_always(nbuf->data == rx_tlv_hdr);
+	}
+
 	/*
 	 * Check if DMA completed -- msdu_done is the last bit
 	 * to be written
 	 */
 	if (!hal_rx_attn_msdu_done_get(rx_tlv_hdr)) {
 
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-				FL("MSDU DONE failure"));
-
+		dp_err_rl("MSDU DONE failure");
 		hal_rx_dump_pkt_tlvs(soc->hal_soc, rx_tlv_hdr,
 				     QDF_TRACE_LEVEL_INFO);
 		qdf_assert(0);
@@ -684,17 +715,14 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 	if (!peer &&
 	    dp_rx_null_q_handle_invalid_peer_id_exception(soc, pool_id,
 							  rx_tlv_hdr, nbuf))
-		return;
+		return QDF_STATUS_E_FAILURE;
 
 	if (!peer) {
 		bool mpdu_done = false;
 		struct dp_pdev *pdev = soc->pdev_list[pool_id];
 
-		QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_DP, "peer is NULL");
-
-		DP_STATS_INC_PKT(soc,
-				 rx.err.rx_invalid_peer,
-				 1,
+		dp_err_rl("peer is NULL");
+		DP_STATS_INC_PKT(soc, rx.err.rx_invalid_peer, 1,
 				 qdf_nbuf_len(nbuf));
 
 		mpdu_done = dp_rx_chain_msdus(soc, nbuf, rx_tlv_hdr, pool_id);
@@ -707,17 +735,14 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 			pdev->invalid_peer_head_msdu = NULL;
 			pdev->invalid_peer_tail_msdu = NULL;
 		}
-		return;
+		return QDF_STATUS_E_FAILURE;
 	}
 
 	vdev = peer->vdev;
 	if (!vdev) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-				FL("INVALID vdev %pK OR osif_rx"), vdev);
-		/* Drop & free packet */
-		qdf_nbuf_free(nbuf);
+		dp_err_rl("Null vdev!");
 		DP_STATS_INC(soc, rx.err.invalid_vdev, 1);
-		return;
+		goto drop_nbuf;
 	}
 
 	/*
@@ -732,9 +757,9 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 	if (dp_rx_mcast_echo_check(soc, peer, rx_tlv_hdr, nbuf)) {
 		/* this is a looped back MCBC pkt, drop it */
 		DP_STATS_INC_PKT(peer, rx.mec_drop, 1, qdf_nbuf_len(nbuf));
-		qdf_nbuf_free(nbuf);
-		return;
+		goto drop_nbuf;
 	}
+
 	/*
 	 * In qwrap mode if the received packet matches with any of the vdev
 	 * mac addresses, drop it. Donot receive multicast packets originated
@@ -742,30 +767,21 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 	 */
 	if (check_qwrap_multicast_loopback(vdev, nbuf)) {
 		DP_STATS_INC_PKT(peer, rx.mec_drop, 1, qdf_nbuf_len(nbuf));
-		qdf_nbuf_free(nbuf);
-		return;
+		goto drop_nbuf;
 	}
 
 
 	if (qdf_unlikely((peer->nawds_enabled == true) &&
 			hal_rx_msdu_end_da_is_mcbc_get(rx_tlv_hdr))) {
-		QDF_TRACE(QDF_MODULE_ID_DP,
-					QDF_TRACE_LEVEL_DEBUG,
-					"%s free buffer for multicast packet",
-					 __func__);
+		dp_err_rl("free buffer for multicast packet");
 		DP_STATS_INC(peer, rx.nawds_mcast_drop, 1);
-		qdf_nbuf_free(nbuf);
-		return;
+		goto drop_nbuf;
 	}
 
 	if (!dp_wds_rx_policy_check(rx_tlv_hdr, vdev, peer,
 				hal_rx_msdu_end_da_is_mcbc_get(rx_tlv_hdr))) {
-		QDF_TRACE(QDF_MODULE_ID_DP,
-				QDF_TRACE_LEVEL_ERROR,
-				FL("mcast Policy Check Drop pkt"));
-		/* Drop & free packet */
-		qdf_nbuf_free(nbuf);
-		return;
+		dp_err_rl("mcast Policy Check Drop pkt");
+		goto drop_nbuf;
 	}
 
 	/* WDS Source Port Learning */
@@ -784,36 +800,18 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 		}
 	}
 
-#ifdef QCA_WIFI_NAPIER_EMULATION /* Debug code, remove later */
-	QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-		  "%s: mac_add:%pM msdu_len %d hdr_off %d",
-		  __func__, peer->mac_addr.raw, msdu_len,
-		  l2_hdr_offset);
-
-	print_hex_dump(KERN_ERR, "\t Pkt Data:", DUMP_PREFIX_NONE, 32, 4,
-					qdf_nbuf_data(nbuf), 128, false);
-#endif /* NAPIER_EMULATION */
-
 	if (qdf_unlikely(vdev->rx_decap_type == htt_cmn_pkt_type_raw)) {
 		qdf_nbuf_set_next(nbuf, NULL);
 		dp_rx_deliver_raw(vdev, nbuf, peer);
 	} else {
 		if (qdf_unlikely(peer->bss_peer)) {
-			QDF_TRACE(QDF_MODULE_ID_DP,
-					QDF_TRACE_LEVEL_INFO,
-					FL("received pkt with same src MAC"));
+			dp_info_rl("received pkt with same src MAC");
 			DP_STATS_INC_PKT(peer, rx.mec_drop, 1,
 					 qdf_nbuf_len(nbuf));
-
-			/* Drop & free packet */
-			qdf_nbuf_free(nbuf);
-			return;
+			goto drop_nbuf;
 		}
 
 		if (vdev->osif_rx) {
-			QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_INFO,
-				FL("vdev %pK osif_rx %pK"), vdev,
-				vdev->osif_rx);
 			qdf_nbuf_set_next(nbuf, NULL);
 			DP_STATS_INC_PKT(peer, rx.to_stack, 1,
 					 qdf_nbuf_len(nbuf));
@@ -835,12 +833,16 @@ dp_rx_null_q_desc_handle(struct dp_soc *soc, qdf_nbuf_t nbuf,
 			vdev->osif_rx(vdev->osif_vdev, nbuf);
 
 		} else {
-			QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-				FL("INVALID vdev %pK OR osif_rx"), vdev);
+			dp_err_rl("INVALID osif_rx. vdev %pK", vdev);
 			DP_STATS_INC(soc, rx.err.invalid_vdev, 1);
+			goto drop_nbuf;
 		}
 	}
-	return;
+	return QDF_STATUS_SUCCESS;
+
+drop_nbuf:
+	qdf_nbuf_free(nbuf);
+	return QDF_STATUS_E_FAILURE;
 }
 
 /**
@@ -1025,22 +1027,19 @@ void dp_rx_process_mic_error(struct dp_soc *soc, qdf_nbuf_t nbuf,
 	wh = (struct ieee80211_frame *)rx_pkt_hdr;
 
 	if (!peer) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-			  "peer not found");
+		dp_err_rl("peer not found");
 		goto fail;
 	}
 
 	vdev = peer->vdev;
 	if (!vdev) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-				"VDEV not found");
+		dp_err_rl("VDEV not found");
 		goto fail;
 	}
 
 	pdev = vdev->pdev;
 	if (!pdev) {
-		QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR,
-				"PDEV not found");
+		dp_err_rl("PDEV not found");
 		goto fail;
 	}
 
@@ -1055,10 +1054,8 @@ void dp_rx_process_mic_error(struct dp_soc *soc, qdf_nbuf_t nbuf,
 	if (fragno) {
 		status = dp_rx_defrag_add_last_frag(soc, peer,
 						    tid, rx_seq, nbuf);
-
-		QDF_TRACE(QDF_MODULE_ID_TXRX, QDF_TRACE_LEVEL_ERROR,
-			  "%s: Frag pkt seq# %d frag# %d consumed status %d !",
-				__func__, rx_seq, fragno, status);
+		dp_info_rl("Frag pkt seq# %d frag# %d consumed status %d !",
+			   rx_seq, fragno, status);
 			return;
 	}
 
@@ -1435,6 +1432,7 @@ done:
 		dp_set_rx_queue(nbuf, 0);
 
 		next = nbuf->next;
+
 		if (wbm_err_info.wbm_err_src == HAL_RX_WBM_ERR_SRC_REO) {
 			if (wbm_err_info.reo_psh_rsn
 				== HAL_RX_WBM_REO_PSH_RSN_ERROR) {
@@ -1450,9 +1448,6 @@ done:
 				 */
 				case HAL_REO_ERR_QUEUE_DESC_ADDR_0:
 					pool_id = wbm_err_info.pool_id;
-					QDF_TRACE_DEBUG_RL(QDF_MODULE_ID_DP,
-						"Got pkt with REO ERROR: %d",
-						wbm_err_info.reo_err_code);
 					dp_rx_null_q_desc_handle(soc, nbuf,
 								 rx_tlv_hdr,
 								 pool_id, peer);
@@ -1465,10 +1460,7 @@ done:
 				/* Add per error code accounting */
 				case HAL_REO_ERR_REGULAR_FRAME_2K_JUMP:
 					pool_id = wbm_err_info.pool_id;
-					QDF_TRACE(QDF_MODULE_ID_DP,
-						  QDF_TRACE_LEVEL_ERROR,
-						"Got pkt with REO ERROR: %d",
-						wbm_err_info.reo_err_code);
+
 					if (hal_rx_msdu_end_first_msdu_get(rx_tlv_hdr)) {
 						peer_id =
 						hal_rx_mpdu_start_sw_peer_id_get(rx_tlv_hdr);
@@ -1483,10 +1475,9 @@ done:
 									peer);
 					continue;
 				default:
-					QDF_TRACE(QDF_MODULE_ID_DP,
-						QDF_TRACE_LEVEL_ERROR,
-						"REO error %d detected",
-						wbm_err_info.reo_err_code);
+					dp_err_rl("Got pkt with REO ERROR: %d",
+						  wbm_err_info.reo_err_code);
+					break;
 				}
 			}
 		} else if (wbm_err_info.wbm_err_src ==
@@ -1530,11 +1521,8 @@ done:
 					break;
 
 				default:
-					QDF_TRACE(QDF_MODULE_ID_DP,
-							QDF_TRACE_LEVEL_DEBUG,
-							"RXDMA error %d",
-							wbm_err_info.
-								rxdma_err_code);
+					dp_err_rl("RXDMA error %d",
+						  wbm_err_info.rxdma_err_code);
 				}
 			}
 		} else {

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

@@ -618,6 +618,8 @@ struct dp_soc_stats {
 			struct cdp_pkt_info rx_invalid_peer;
 			/* Invalid PEER ID count */
 			struct cdp_pkt_info rx_invalid_peer_id;
+			/* Invalid packet length */
+			struct cdp_pkt_info rx_invalid_pkt_len;
 			/* HAL ring access Fail error count */
 			uint32_t hal_ring_access_fail;
 			/* RX DMA error count */