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
This commit is contained in:
Mohit Khanna
2019-04-02 14:43:10 -07:00
committed by nshrivas
parent 13da9a8463
commit f085b61b59
3 changed files with 81 additions and 89 deletions

View File

@@ -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");

View File

@@ -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 {

View File

@@ -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 */