From f1c577ee031a3a8fba30c9f020b9290b773f59ef Mon Sep 17 00:00:00 2001 From: Lin Bai Date: Tue, 22 May 2018 20:45:42 +0800 Subject: [PATCH] qcacmn: Cleanup defrag waitlist when removing peer Peer's RX TID may already be added to defrag's waitlist when it is removed. Then use-after-free occurs when waitlist be accessed later. Remove current peer's entries from waitlist, before it is freed. As waitlist may be accessed from two contexts, additional lock introduced to protect waitlist. CRs-Fixed: 2244566 Change-Id: I8f1935973372a81086e9dbd992ac771b6709a677 --- dp/wifi3.0/dp_main.c | 3 +++ dp/wifi3.0/dp_peer.c | 6 ++++++ dp/wifi3.0/dp_rx_defrag.c | 29 +++++++++++++++++++++++------ dp/wifi3.0/dp_types.h | 1 + 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/dp/wifi3.0/dp_main.c b/dp/wifi3.0/dp_main.c index 8ef37679e9e7..fdcce494a511 100644 --- a/dp/wifi3.0/dp_main.c +++ b/dp/wifi3.0/dp_main.c @@ -2253,6 +2253,7 @@ static int dp_soc_cmn_setup(struct dp_soc *soc) wlan_cfg_get_rx_defrag_min_timeout(soc->wlan_cfg_ctx); soc->rx.flags.defrag_timeout_check = wlan_cfg_get_defrag_timeout_check(soc->wlan_cfg_ctx); + qdf_spinlock_create(&soc->rx.defrag.defrag_lock); out: /* @@ -3048,6 +3049,8 @@ static void dp_soc_detach_wifi3(void *txrx_soc) htt_soc_detach(soc->htt_handle); + qdf_spinlock_destroy(&soc->rx.defrag.defrag_lock); + dp_reo_cmdlist_destroy(soc); qdf_spinlock_destroy(&soc->rx.reo_cmd_lock); dp_reo_desc_freelist_destroy(soc); diff --git a/dp/wifi3.0/dp_peer.c b/dp/wifi3.0/dp_peer.c index a2474e56894b..d947e46be1f9 100644 --- a/dp/wifi3.0/dp_peer.c +++ b/dp/wifi3.0/dp_peer.c @@ -22,6 +22,7 @@ #include "dp_types.h" #include "dp_internal.h" #include "dp_peer.h" +#include "dp_rx_defrag.h" #include #include #ifdef CONFIG_MCL @@ -1570,6 +1571,11 @@ void dp_peer_rx_cleanup(struct dp_vdev *vdev, struct dp_peer *peer) for (tid = 0; tid < DP_MAX_TIDS; tid++) { if (peer->rx_tid[tid].hw_qdesc_vaddr_unaligned != NULL) { dp_rx_tid_delete_wifi3(peer, tid); + + /* Cleanup defrag related resource */ + dp_rx_defrag_waitlist_remove(peer, tid); + dp_rx_reorder_flush_frag(peer, tid); + tid_delete_mask |= (1 << tid); } } diff --git a/dp/wifi3.0/dp_rx_defrag.c b/dp/wifi3.0/dp_rx_defrag.c index f2a63a654813..0f35d9cd59d2 100644 --- a/dp/wifi3.0/dp_rx_defrag.c +++ b/dp/wifi3.0/dp_rx_defrag.c @@ -150,10 +150,13 @@ void dp_rx_defrag_waitlist_flush(struct dp_soc *soc) struct dp_rx_tid *rx_reorder; struct dp_rx_tid *tmp; uint32_t now_ms = qdf_system_ticks_to_msecs(qdf_system_ticks()); + TAILQ_HEAD(, dp_rx_tid) temp_list; + TAILQ_INIT(&temp_list); + + qdf_spin_lock_bh(&soc->rx.defrag.defrag_lock); TAILQ_FOREACH_SAFE(rx_reorder, &soc->rx.defrag.waitlist, defrag_waitlist_elem, tmp) { - struct dp_peer *peer; unsigned int tid; QDF_TRACE(QDF_MODULE_ID_DP, QDF_TRACE_LEVEL_ERROR, @@ -170,14 +173,24 @@ void dp_rx_defrag_waitlist_flush(struct dp_soc *soc) continue; } + TAILQ_REMOVE(&soc->rx.defrag.waitlist, rx_reorder, + defrag_waitlist_elem); + + /* Move to temp list and clean-up later */ + TAILQ_INSERT_TAIL(&temp_list, rx_reorder, + defrag_waitlist_elem); + } + qdf_spin_unlock_bh(&soc->rx.defrag.defrag_lock); + + TAILQ_FOREACH_SAFE(rx_reorder, &temp_list, + defrag_waitlist_elem, tmp) { + struct dp_peer *peer; + /* get address of current peer */ peer = container_of(rx_reorder, struct dp_peer, - rx_tid[tid]); - - TAILQ_REMOVE(&soc->rx.defrag.waitlist, rx_reorder, - defrag_waitlist_elem); - dp_rx_reorder_flush_frag(peer, tid); + rx_tid[rx_reorder->tid]); + dp_rx_reorder_flush_frag(peer, rx_reorder->tid); } } @@ -200,8 +213,10 @@ static void dp_rx_defrag_waitlist_add(struct dp_peer *peer, unsigned tid) tid, peer); /* TODO: use LIST macros instead of TAIL macros */ + qdf_spin_lock_bh(&psoc->rx.defrag.defrag_lock); TAILQ_INSERT_TAIL(&psoc->rx.defrag.waitlist, rx_reorder, defrag_waitlist_elem); + qdf_spin_unlock_bh(&psoc->rx.defrag.defrag_lock); } /* @@ -230,6 +245,7 @@ void dp_rx_defrag_waitlist_remove(struct dp_peer *peer, unsigned tid) FL("Remove TID %u from waitlist for peer %pK"), tid, peer); + qdf_spin_lock_bh(&soc->rx.defrag.defrag_lock); TAILQ_FOREACH(rx_reorder, &soc->rx.defrag.waitlist, defrag_waitlist_elem) { struct dp_peer *peer_on_waitlist; @@ -244,6 +260,7 @@ void dp_rx_defrag_waitlist_remove(struct dp_peer *peer, unsigned tid) TAILQ_REMOVE(&soc->rx.defrag.waitlist, rx_reorder, defrag_waitlist_elem); } + qdf_spin_unlock_bh(&soc->rx.defrag.defrag_lock); } /* diff --git a/dp/wifi3.0/dp_types.h b/dp/wifi3.0/dp_types.h index 8251fbb7c9ad..4fcfe2d059e5 100644 --- a/dp/wifi3.0/dp_types.h +++ b/dp/wifi3.0/dp_types.h @@ -755,6 +755,7 @@ struct dp_soc { struct { TAILQ_HEAD(, dp_rx_tid) waitlist; uint32_t timeout_ms; + qdf_spinlock_t defrag_lock; } defrag; struct { int defrag_timeout_check;