Browse Source

qcacmn: Correct pkt length check for scatter frame rx

When a scattered frame is received, msdu length caclulation
is incorrect, this leads to while loop can't break. At last
skb, null pointer dereference happned. Change is aimed to
correct the length caclulation.

Change-Id: Ifb316f004ea5829b1130ce4c0fc9253134e26713
CRs-Fixed: 3025973
Yu Tian 3 years ago
parent
commit
9fa8dd3bc3
1 changed files with 59 additions and 11 deletions
  1. 59 11
      dp/wifi3.0/dp_rx.c

+ 59 - 11
dp/wifi3.0/dp_rx.c

@@ -1134,27 +1134,64 @@ void dp_rx_fill_gro_info(struct dp_soc *soc, uint8_t *rx_tlv,
  * @soc: DP soc handle
  * @nbuf: pointer to msdu.
  * @mpdu_len: mpdu length
+ * @l3_pad_len: L3 padding length by HW
  *
  * Return: returns true if nbuf is last msdu of mpdu else retuns false.
  */
 static inline bool dp_rx_adjust_nbuf_len(struct dp_soc *soc,
-					 qdf_nbuf_t nbuf, uint16_t *mpdu_len)
+					 qdf_nbuf_t nbuf,
+					 uint16_t *mpdu_len,
+					 uint32_t l3_pad_len)
 {
 	bool last_nbuf;
+	uint32_t pkt_hdr_size;
 
-	if (*mpdu_len > (RX_DATA_BUFFER_SIZE - soc->rx_pkt_tlv_size)) {
+	pkt_hdr_size = soc->rx_pkt_tlv_size + l3_pad_len;
+
+	if ((*mpdu_len + pkt_hdr_size) > RX_DATA_BUFFER_SIZE) {
 		qdf_nbuf_set_pktlen(nbuf, RX_DATA_BUFFER_SIZE);
 		last_nbuf = false;
+		*mpdu_len -= (RX_DATA_BUFFER_SIZE - pkt_hdr_size);
 	} else {
-		qdf_nbuf_set_pktlen(nbuf, (*mpdu_len + soc->rx_pkt_tlv_size));
+		qdf_nbuf_set_pktlen(nbuf, (*mpdu_len + pkt_hdr_size));
 		last_nbuf = true;
+		*mpdu_len = 0;
 	}
 
-	*mpdu_len -= (RX_DATA_BUFFER_SIZE - soc->rx_pkt_tlv_size);
-
 	return last_nbuf;
 }
 
+/**
+ * dp_get_l3_hdr_pad_len() - get L3 header padding length.
+ *
+ * @soc: DP soc handle
+ * @nbuf: pointer to msdu.
+ *
+ * Return: returns padding length in bytes.
+ */
+static inline uint32_t dp_get_l3_hdr_pad_len(struct dp_soc *soc,
+					     qdf_nbuf_t nbuf)
+{
+	uint32_t l3_hdr_pad = 0;
+	uint8_t *rx_tlv_hdr;
+	struct hal_rx_msdu_metadata msdu_metadata;
+
+	while (nbuf) {
+		if (!qdf_nbuf_is_rx_chfrag_cont(nbuf)) {
+			/* scattered msdu end with continuation is 0 */
+			rx_tlv_hdr = qdf_nbuf_data(nbuf);
+			hal_rx_msdu_metadata_get(soc->hal_soc,
+						 rx_tlv_hdr,
+						 &msdu_metadata);
+			l3_hdr_pad = msdu_metadata.l3_hdr_pad;
+			break;
+		}
+		nbuf = nbuf->next;
+	}
+
+	return l3_hdr_pad;
+}
+
 /**
  * dp_rx_sg_create() - create a frag_list for MSDUs which are spread across
  *		     multiple nbufs.
@@ -1172,6 +1209,7 @@ qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf)
 	uint16_t frag_list_len = 0;
 	uint16_t mpdu_len;
 	bool last_nbuf;
+	uint32_t l3_hdr_pad_offset = 0;
 
 	/*
 	 * Use msdu len got from REO entry descriptor instead since
@@ -1179,6 +1217,7 @@ qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf)
 	 * from REO descriptor is right for non-raw RX scatter msdu.
 	 */
 	mpdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf);
+
 	/*
 	 * this is a case where the complete msdu fits in one single nbuf.
 	 * in this case HW sets both start and end bit and we only need to
@@ -1191,6 +1230,7 @@ qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf)
 		return nbuf;
 	}
 
+	l3_hdr_pad_offset = dp_get_l3_hdr_pad_len(soc, nbuf);
 	/*
 	 * This is a case where we have multiple msdus (A-MSDU) spread across
 	 * multiple nbufs. here we create a fraglist out of these nbufs.
@@ -1210,17 +1250,23 @@ qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf)
 	 * nbufs will form the frag_list of the parent nbuf.
 	 */
 	qdf_nbuf_set_rx_chfrag_start(parent, 1);
-	last_nbuf = dp_rx_adjust_nbuf_len(soc, parent, &mpdu_len);
+	/*
+	 * L3 header padding is only needed for the 1st buffer
+	 * in a scattered msdu
+	 */
+	last_nbuf = dp_rx_adjust_nbuf_len(soc, parent, &mpdu_len,
+					  l3_hdr_pad_offset);
 
 	/*
-	 * HW issue:  MSDU cont bit is set but reported MPDU length can fit
+	 * MSDU cont bit is set but reported MPDU length can fit
 	 * in to single buffer
 	 *
 	 * Increment error stats and avoid SG list creation
 	 */
 	if (last_nbuf) {
 		DP_STATS_INC(soc, rx.err.msdu_continuation_err, 1);
-		qdf_nbuf_pull_head(parent, soc->rx_pkt_tlv_size);
+		qdf_nbuf_pull_head(parent,
+				   soc->rx_pkt_tlv_size + l3_hdr_pad_offset);
 		return parent;
 	}
 
@@ -1230,8 +1276,9 @@ qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf)
 	 * till we hit the last_nbuf of the list.
 	 */
 	do {
-		last_nbuf = dp_rx_adjust_nbuf_len(soc, nbuf, &mpdu_len);
-		qdf_nbuf_pull_head(nbuf, soc->rx_pkt_tlv_size);
+		last_nbuf = dp_rx_adjust_nbuf_len(soc, nbuf, &mpdu_len, 0);
+		qdf_nbuf_pull_head(nbuf,
+				   soc->rx_pkt_tlv_size);
 		frag_list_len += qdf_nbuf_len(nbuf);
 
 		if (last_nbuf) {
@@ -1247,7 +1294,8 @@ qdf_nbuf_t dp_rx_sg_create(struct dp_soc *soc, qdf_nbuf_t nbuf)
 	qdf_nbuf_append_ext_list(parent, frag_list, frag_list_len);
 	parent->next = next;
 
-	qdf_nbuf_pull_head(parent, soc->rx_pkt_tlv_size);
+	qdf_nbuf_pull_head(parent,
+			   soc->rx_pkt_tlv_size + l3_hdr_pad_offset);
 	return parent;
 }