From 3222184e140ec7709df0ba355345b3250fcfc884 Mon Sep 17 00:00:00 2001 From: Jinwei Chen Date: Thu, 16 Jan 2020 19:52:41 +0800 Subject: [PATCH] qcacmn: fix RX desc is using but freed back to pool Crash scenario: (1) frag data A is dropped and related RX desc A is replenished and reused, but pdev->free_list_tail is still pointed to RX desc A. (2) frag data B/C is coming, defrag fails then pdev->free_list_head will point to B-->C RX desc, but pdev->free_list_tail still point to A. (3) for defrag failing case, host only will replenish 1 RX buffer for current case, RX desc B is replenished, while C will be free back to RX desc pool. (4) dp_rx_add_desc_list_to_free_list will set RX desc A-->next = free_list, free_list point to C insted. (5) when step (1) RX desc A replenished buffer indicated to host by REO2Dst ring, RX desc A -->nbuf actually is pointed to another RX desc, invalid skb accessing will happen. Solution: a. reset tail pointer in dp_rx_add_desc_list_to_free_list at last. b. reset tail pointer same as head in dp_rx_add_to_free_desc_list if head->next is NULL. c. set correct rx_bufs number for replenish when dp_rx_defrag fails. Change-Id: Ib297baea3605a09dd7d85d1f5ceb95db48a2e1f1 CRs-Fixed: 2603676 --- dp/wifi3.0/dp_rx.h | 3 ++- dp/wifi3.0/dp_rx_defrag.c | 10 +++++----- dp/wifi3.0/dp_rx_desc.c | 3 ++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/dp/wifi3.0/dp_rx.h b/dp/wifi3.0/dp_rx.h index 56c8d24d6f..38ef370092 100644 --- a/dp/wifi3.0/dp_rx.h +++ b/dp/wifi3.0/dp_rx.h @@ -582,7 +582,8 @@ void dp_rx_add_to_free_desc_list(union dp_rx_desc_list_elem_t **head, ((union dp_rx_desc_list_elem_t *)new)->next = *head; *head = (union dp_rx_desc_list_elem_t *)new; - if (!*tail) + /* reset tail if head->next is NULL */ + if (!*tail || !(*head)->next) *tail = *head; } diff --git a/dp/wifi3.0/dp_rx_defrag.c b/dp/wifi3.0/dp_rx_defrag.c index 83a64572c0..310f4df4fe 100644 --- a/dp/wifi3.0/dp_rx_defrag.c +++ b/dp/wifi3.0/dp_rx_defrag.c @@ -1578,7 +1578,7 @@ dp_rx_defrag_store_fragment(struct dp_soc *soc, } } else { dp_rx_add_to_free_desc_list(head, tail, rx_desc); - *rx_bfs = 1; + (*rx_bfs)++; /* Return the non-head link desc */ if (ring_desc && @@ -1619,7 +1619,7 @@ dp_rx_defrag_store_fragment(struct dp_soc *soc, dp_rx_add_to_free_desc_list(head, tail, peer->rx_tid[tid].head_frag_desc); - *rx_bfs = 1; + (*rx_bfs)++; if (dp_rx_link_desc_return(soc, peer->rx_tid[tid].dst_ring_desc, @@ -1660,7 +1660,7 @@ discard_frag: QDF_STATUS_SUCCESS) QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, "%s: Failed to return link desc", __func__); - *rx_bfs = 1; + (*rx_bfs)++; end: if (peer) @@ -1698,7 +1698,7 @@ uint32_t dp_rx_frag_handle(struct dp_soc *soc, hal_ring_desc_t ring_desc, uint32_t rx_bufs_used = 0; qdf_nbuf_t msdu = NULL; uint32_t tid; - int rx_bfs = 0; + uint32_t rx_bfs = 0; struct dp_pdev *pdev; QDF_STATUS status = QDF_STATUS_SUCCESS; @@ -1737,7 +1737,7 @@ uint32_t dp_rx_frag_handle(struct dp_soc *soc, hal_ring_desc_t ring_desc, tid, rx_desc, &rx_bfs); if (rx_bfs) - rx_bufs_used++; + rx_bufs_used += rx_bfs; if (!QDF_IS_STATUS_SUCCESS(status)) dp_info_rl("Rx Defrag err seq#:0x%x msdu_count:%d flags:%d", diff --git a/dp/wifi3.0/dp_rx_desc.c b/dp/wifi3.0/dp_rx_desc.c index ddf5a70a37..387aa0064d 100644 --- a/dp/wifi3.0/dp_rx_desc.c +++ b/dp/wifi3.0/dp_rx_desc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2016-2019 The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020 The Linux Foundation. All rights reserved. * * Permission to use, copy, modify, and/or distribute this software for * any purpose with or without fee is hereby granted, provided that the @@ -331,6 +331,7 @@ void dp_rx_add_desc_list_to_free_list(struct dp_soc *soc, temp_list, *local_desc_list, *tail, (*tail)->next); rx_desc_pool->freelist = *local_desc_list; (*tail)->next = temp_list; + *tail = NULL; qdf_spin_unlock_bh(&rx_desc_pool->lock); }