From f085b61b5986d0cda24dbdb5992da7670e3f5e4d Mon Sep 17 00:00:00 2001 From: Mohit Khanna Date: Tue, 2 Apr 2019 14:43:10 -0700 Subject: [PATCH] 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 --- dp/wifi3.0/dp_main.c | 4 +- dp/wifi3.0/dp_rx_err.c | 164 +++++++++++++++++++---------------------- dp/wifi3.0/dp_types.h | 2 + 3 files changed, 81 insertions(+), 89 deletions(-) diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index 208466253a..1baba7ad62 100644 --- a/dp/wifi3.0/dp_main.c +++ b/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"); diff --git a/dp/wifi3.0/dp_rx_err.c b/dp/wifi3.0/dp_rx_err.c index 2d9fd4c73e..00ae994045 100644 --- a/dp/wifi3.0/dp_rx_err.c +++ b/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 { diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 13a41d432d..9d2145c640 100644 --- a/dp/wifi3.0/dp_types.h +++ b/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 */