From 69a11f24b166e8370454e0755c6efe34da2079b2 Mon Sep 17 00:00:00 2001 From: Alok Kumar Date: Wed, 11 Dec 2019 15:30:34 +0530 Subject: [PATCH] qcacld-3.0: Fix double free of SKB for TSO In case of TSO, same skb results in multiple tx_desc after segmentation. To avoid multiple free of skb "skb->users" count is used. Currently, race condition is observed while incrementing "skb->users" count in ol_tx_ll_fast and before that, tx_completion is received for all the segments which are sent before. In this case, "skb->users" count will be 1 so, skb is removed from the skb debug node which leads double free of skb when tx completion is received for the last segment. To fix this, increment the "skb->users" count before sending first segment and decrement only iwhen the last segment. Change-Id: I26000fcda8715b737cac803ec8e64c0fb640dfe4 CRs-Fixed: 2582617 --- core/dp/txrx/ol_tx_ll_fastpath.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/core/dp/txrx/ol_tx_ll_fastpath.c b/core/dp/txrx/ol_tx_ll_fastpath.c index b5a5b0d8b264..3841c02742a2 100644 --- a/core/dp/txrx/ol_tx_ll_fastpath.c +++ b/core/dp/txrx/ol_tx_ll_fastpath.c @@ -382,6 +382,15 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) * pointer before the ce_send call. */ next = qdf_nbuf_next(msdu); + /* + * Increment the skb->users count here, for this SKB, to make + * sure it will be freed only after receiving Tx completion + * of the last segment. + * Decrement skb->users count before sending last segment + */ + if (qdf_nbuf_is_tso(msdu) && segments) + qdf_nbuf_inc_users(msdu); + /* init the current segment to the 1st segment in the list */ while (segments) { if (msdu_info.tso_info.curr_seg) @@ -451,6 +460,13 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) next_seg = NULL; } + /* Decrement the skb-users count if segment + * is the last segment or the only segment + */ + if (tx_desc->pkt_type == OL_TX_FRM_TSO && + segments == 0) + qdf_nbuf_tx_free(msdu, 0); + if ((ce_send_fast(pdev->ce_tx_hdl, msdu, ep_id, pkt_download_len) == 0)) { @@ -465,6 +481,10 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) tso_info->curr_seg = next_seg; ol_free_remaining_tso_segs(vdev, &msdu_info, true); + if (segments) + qdf_nbuf_tx_free( + msdu, + QDF_NBUF_PKT_ERROR); } /* @@ -491,10 +511,12 @@ ol_tx_ll_fast(ol_txrx_vdev_handle vdev, qdf_nbuf_t msdu_list) * If TSO packet, free associated * remaining TSO segment descriptors */ - if (qdf_nbuf_is_tso(msdu)) + if (qdf_nbuf_is_tso(msdu)) { ol_free_remaining_tso_segs(vdev, &msdu_info, true); - + qdf_nbuf_tx_free(msdu, + QDF_NBUF_PKT_ERROR); + } TXRX_STATS_MSDU_LIST_INCR( pdev, tx.dropped.host_reject, msdu); /* the list of unaccepted MSDUs */