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:
@@ -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,
|
||||
|
Reference in New Issue
Block a user