qcacmn: fix invalid accessing to rx_tlv_hdr due to scattered msdu

long length msdu is received and looks this msdu is spread across
multiple nbufs, there is no corresbonding logic for this case.
qdf_set_pkt_len will invoke pskb_expand_head to renew skb->head
buffer, but the rx_tlv_hdr is still pointed to original skb->data
buffer, invalid accessing will happen.
  As a WAR, drop this msdu related nbufs after dp_rx_sg_create is done.

Change-Id: Iceb09fd04e4d768325018a8ddd4261ab4f75991a
CRs-Fixed: 2597927
This commit is contained in:
Jinwei Chen
2020-01-14 13:52:06 +08:00
committed by nshrivas
parent f6485c72bb
commit 0b92469595
4 changed files with 53 additions and 22 deletions

View File

@@ -1031,22 +1031,25 @@ static inline bool dp_rx_adjust_nbuf_len(qdf_nbuf_t nbuf, uint16_t *mpdu_len)
* dp_rx_sg_create() - create a frag_list for MSDUs which are spread across
* multiple nbufs.
* @nbuf: pointer to the first msdu of an amsdu.
* @rx_tlv_hdr: pointer to the start of RX TLV headers.
*
*
* This function implements the creation of RX frag_list for cases
* where an MSDU is spread across multiple nbufs.
*
* Return: returns the head nbuf which contains complete frag_list.
*/
qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf, uint8_t *rx_tlv_hdr)
qdf_nbuf_t dp_rx_sg_create(qdf_nbuf_t nbuf)
{
qdf_nbuf_t parent, frag_list, next = NULL;
uint16_t frag_list_len = 0;
uint16_t mpdu_len;
bool last_nbuf;
mpdu_len = hal_rx_msdu_start_msdu_len_get(rx_tlv_hdr);
/*
* Use msdu len got from REO entry descriptor instead since
* there is case the RX PKT TLV is corrupted while msdu_len
* 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
@@ -1881,13 +1884,12 @@ more_data:
if (mpdu_desc_info.mpdu_flags & HAL_MPDU_F_RETRY_BIT)
qdf_nbuf_set_rx_retry_flag(rx_desc->nbuf, 1);
if (qdf_unlikely(mpdu_desc_info.mpdu_flags &
HAL_MPDU_F_RAW_AMPDU)) {
if (qdf_unlikely(msdu_desc_info.msdu_flags &
HAL_MSDU_F_MSDU_CONTINUATION)) {
/* previous msdu has end bit set, so current one is
* the new MPDU
*/
if (is_prev_msdu_last) {
is_prev_msdu_last = false;
/* Get number of entries available in HW ring */
num_entries_avail =
hal_srng_dst_num_valid(hal_soc,
@@ -1900,16 +1902,26 @@ more_data:
*/
if (((msdu_desc_info.msdu_len /
(RX_BUFFER_SIZE - RX_PKT_TLVS_LEN) + 1)) >
num_entries_avail)
num_entries_avail) {
DP_STATS_INC(
soc,
rx.msdu_scatter_wait_break,
1);
break;
} else {
if (msdu_desc_info.msdu_flags &
HAL_MSDU_F_LAST_MSDU_IN_MPDU)
is_prev_msdu_last = true;
}
is_prev_msdu_last = false;
}
qdf_nbuf_set_raw_frame(rx_desc->nbuf, 1);
}
if (qdf_unlikely(mpdu_desc_info.mpdu_flags &
HAL_MPDU_F_RAW_AMPDU))
qdf_nbuf_set_raw_frame(rx_desc->nbuf, 1);
if (!is_prev_msdu_last &&
msdu_desc_info.msdu_flags & HAL_MSDU_F_LAST_MSDU_IN_MPDU)
is_prev_msdu_last = true;
/* Pop out the descriptor*/
hal_srng_dst_get_next(hal_soc, hal_ring_hdl);
@@ -2064,7 +2076,7 @@ done:
* Check if DMA completed -- msdu_done is the last bit
* to be written
*/
if (qdf_unlikely(!qdf_nbuf_is_raw_frame(nbuf) &&
if (qdf_unlikely(!qdf_nbuf_is_rx_chfrag_cont(nbuf) &&
!hal_rx_attn_msdu_done_get(rx_tlv_hdr))) {
dp_err("MSDU DONE failure");
DP_STATS_INC(soc, rx.err.msdu_done_fail, 1);
@@ -2120,14 +2132,23 @@ done:
qdf_nbuf_set_sa_valid(nbuf, is_sa_vld);
qdf_nbuf_pull_head(nbuf, RX_PKT_TLVS_LEN);
} else if (qdf_nbuf_is_raw_frame(nbuf)) {
} else if (qdf_nbuf_is_rx_chfrag_cont(nbuf)) {
msdu_len = QDF_NBUF_CB_RX_PKT_LEN(nbuf);
nbuf = dp_rx_sg_create(nbuf, rx_tlv_hdr);
DP_STATS_INC(vdev->pdev, rx_raw_pkts, 1);
DP_STATS_INC_PKT(peer, rx.raw, 1, msdu_len);
nbuf = dp_rx_sg_create(nbuf);
next = nbuf->next;
if (qdf_nbuf_is_raw_frame(nbuf)) {
DP_STATS_INC(vdev->pdev, rx_raw_pkts, 1);
DP_STATS_INC_PKT(peer, rx.raw, 1, msdu_len);
} else {
qdf_nbuf_free(nbuf);
DP_STATS_INC(soc, rx.err.scatter_msdu, 1);
dp_info_rl("scatter msdu len %d, dropped",
msdu_len);
nbuf = next;
dp_peer_unref_del_find_by_id(peer);
continue;
}
} else {
l2_hdr_offset =
hal_rx_msdu_end_l3_hdr_padding_get(soc->hal_soc,