浏览代码

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
Jinwei Chen 5 年之前
父节点
当前提交
3222184e14
共有 3 个文件被更改,包括 9 次插入7 次删除
  1. 2 1
      dp/wifi3.0/dp_rx.h
  2. 5 5
      dp/wifi3.0/dp_rx_defrag.c
  3. 2 1
      dp/wifi3.0/dp_rx_desc.c

+ 2 - 1
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;
 
 }

+ 5 - 5
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",

+ 2 - 1
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);
 }