From 51ab1164a187a4e2fce3db6ba2d5b3b05681a3f1 Mon Sep 17 00:00:00 2001 From: Bapiraju Alla Date: Tue, 17 Mar 2020 13:35:39 +0530 Subject: [PATCH] qcacld-3.0: Synchronize sta_info access in host driver Currently there is no synchronized access for sta_info. Because of this memory use after free is getting triggered. To resolve this, use a reference count for sta_info to check whether it is in use or not before freeing. Change-Id: I696461a1f53f4cc4739963f3f97ada31d3fa15f3 CRs-Fixed: 2637074 --- core/hdd/src/wlan_hdd_cfg80211.c | 4 +++ core/hdd/src/wlan_hdd_hostapd.c | 18 ++++++++-- core/hdd/src/wlan_hdd_softap_tx_rx.c | 13 ++++++- core/hdd/src/wlan_hdd_sta_info.c | 54 +++++++++++++++++++++++----- core/hdd/src/wlan_hdd_sta_info.h | 14 ++++++++ core/hdd/src/wlan_hdd_station_info.c | 5 +++ core/hdd/src/wlan_hdd_stats.c | 2 ++ 7 files changed, 98 insertions(+), 12 deletions(-) diff --git a/core/hdd/src/wlan_hdd_cfg80211.c b/core/hdd/src/wlan_hdd_cfg80211.c index cd0ad5920e15..5aa2c0cbc38b 100644 --- a/core/hdd/src/wlan_hdd_cfg80211.c +++ b/core/hdd/src/wlan_hdd_cfg80211.c @@ -10399,6 +10399,7 @@ static int __wlan_hdd_cfg80211_get_link_properties(struct wiphy *wiphy, nss = sta_info->nss; freq = (WLAN_HDD_GET_AP_CTX_PTR(adapter))->operating_chan_freq; rate_flags = sta_info->rate_flags; + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); } else { hdd_err("Not Associated! with mac "QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(peer_mac)); @@ -21183,6 +21184,8 @@ int __wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, hdd_debug("Skip DEL STA as deauth is in progress::" QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(mac)); + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, + true); return -ENOENT; } @@ -21190,6 +21193,7 @@ int __wlan_hdd_cfg80211_del_station(struct wiphy *wiphy, QDF_MAC_ADDR_ARRAY(mac)); hdd_softap_deauth_current_sta(adapter, sta_info, hapd_state, param); + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); } fn_end: diff --git a/core/hdd/src/wlan_hdd_hostapd.c b/core/hdd/src/wlan_hdd_hostapd.c index 3509e97c2d46..4dfef92d7a8f 100644 --- a/core/hdd/src/wlan_hdd_hostapd.c +++ b/core/hdd/src/wlan_hdd_hostapd.c @@ -1530,7 +1530,7 @@ static void hdd_fill_station_info(struct hdd_adapter *adapter, if (!cache_sta_info) { cache_sta_info = qdf_mem_malloc(sizeof(*cache_sta_info)); if (!cache_sta_info) - return; + goto exit; qdf_mem_copy(cache_sta_info, stainfo, sizeof(*cache_sta_info)); cache_sta_info->assoc_req_ies.data = @@ -1579,6 +1579,9 @@ static void hdd_fill_station_info(struct hdd_adapter *adapter, hdd_sta_info_attach(&adapter->cache_sta_info_list, cache_sta_info); } + } else { + hdd_put_sta_info(&adapter->cache_sta_info_list, + &cache_sta_info, true); } hdd_debug("cap %d %d %d %d %d %d %d %d %d %x %d", @@ -1599,6 +1602,9 @@ static void hdd_fill_station_info(struct hdd_adapter *adapter, stainfo->max_mcs_idx, stainfo->rx_mcs_map, stainfo->tx_mcs_map); +exit: + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); + return; } void hdd_stop_sap_due_to_invalid_channel(struct work_struct *work) @@ -2219,10 +2225,13 @@ QDF_STATUS hdd_hostapd_sap_event_cb(struct sap_event *sap_event, &adapter->sta_info_list, (uint8_t *)&wrqu.addr.sa_data); - if (stainfo) + if (stainfo) { stainfo->ecsa_capable = event->ecsa_capable; - else + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, + true); + } else { hdd_err("Station not found"); + } if (ucfg_ipa_is_enabled()) { status = ucfg_ipa_wlan_evt(hdd_ctx->pdev, @@ -2325,6 +2334,8 @@ QDF_STATUS hdd_hostapd_sap_event_cb(struct sap_event *sap_event, cache_stainfo->tx_rate, cache_stainfo->rx_rate, cache_stainfo->reason_code); + hdd_put_sta_info(&adapter->cache_sta_info_list, + &cache_stainfo, true); } hdd_nofl_info("SAP disassociated " QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(wrqu.addr.sa_data)); @@ -2361,6 +2372,7 @@ QDF_STATUS hdd_hostapd_sap_event_cb(struct sap_event *sap_event, WMA_DHCP_STOP_IND); stainfo->dhcp_nego_status = DHCP_NEGO_STOP; + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); hdd_softap_deregister_sta(adapter, &stainfo); ap_ctx->ap_active = false; diff --git a/core/hdd/src/wlan_hdd_softap_tx_rx.c b/core/hdd/src/wlan_hdd_softap_tx_rx.c index 55ff0b959215..fb832c8c4a95 100644 --- a/core/hdd/src/wlan_hdd_softap_tx_rx.c +++ b/core/hdd/src/wlan_hdd_softap_tx_rx.c @@ -389,6 +389,7 @@ int hdd_softap_inspect_dhcp_packet(struct hdd_adapter *adapter, hdd_debug("EXIT: phase=%d, nego_status=%d", hdd_sta_info->dhcp_phase, hdd_sta_info->dhcp_nego_status); + hdd_put_sta_info(&adapter->sta_info_list, &hdd_sta_info, true); } return errno; @@ -423,7 +424,7 @@ static void __hdd_softap_hard_start_xmit(struct sk_buff *skb, static struct qdf_mac_addr bcast_mac_addr = QDF_MAC_ADDR_BCAST_INIT; void *soc = cds_get_context(QDF_MODULE_ID_SOC); uint32_t num_seg; - struct hdd_station_info *sta_info; + struct hdd_station_info *sta_info = NULL; ++adapter->hdd_stats.tx_rx_stats.tx_called; adapter->hdd_stats.tx_rx_stats.cont_txtimeout_cnt = 0; @@ -606,12 +607,15 @@ static void __hdd_softap_hard_start_xmit(struct sk_buff *skb, netif_trans_update(dev); wlan_hdd_sar_unsolicited_timer_start(hdd_ctx); + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); return; drop_pkt_and_release_skb: qdf_net_buf_debug_release_skb(skb); drop_pkt: + if (sta_info) + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); qdf_dp_trace_data_pkt(skb, QDF_TRACE_DEFAULT_PDEV_ID, QDF_DP_TRACE_DROP_PACKET_RECORD, 0, @@ -744,6 +748,7 @@ QDF_STATUS hdd_softap_init_tx_rx_sta(struct hdd_adapter *adapter, sta_mac->bytes); if (sta_info) { + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); hdd_err("Reinit of in use station " QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(sta_mac->bytes)); return QDF_STATUS_E_FAILURE; @@ -890,6 +895,8 @@ QDF_STATUS hdd_softap_rx_packet_cbk(void *adapter_context, qdf_nbuf_t rx_buf) sta_info->rx_bytes += skb->len; sta_info->last_tx_rx_ts = qdf_system_ticks(); hdd_softap_inspect_dhcp_packet(adapter, skb, QDF_RX); + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, + true); } hdd_event_eapol_log(skb, QDF_RX); @@ -1040,6 +1047,7 @@ QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter, hdd_debug("clean up old entry for STA MAC " QDF_MAC_ADDR_STR, QDF_MAC_ADDR_ARRAY(sta_mac->bytes)); hdd_softap_deregister_sta(adapter, &sta_info); + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); } /* @@ -1090,6 +1098,7 @@ QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter, if (!QDF_IS_STATUS_SUCCESS(qdf_status)) { hdd_debug("cdp_peer_register() failed to register. Status = %d [0x%08X]", qdf_status, qdf_status); + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); return qdf_status; } @@ -1133,6 +1142,7 @@ QDF_STATUS hdd_softap_register_sta(struct hdd_adapter *adapter, sta_info->peer_state = OL_TXRX_PEER_STATE_CONN; } + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); hdd_debug("Enabling queues"); wlan_hdd_netif_queue_control(adapter, WLAN_START_ALL_NETIF_QUEUE_N_CARRIER, @@ -1256,6 +1266,7 @@ QDF_STATUS hdd_softap_change_sta_state(struct hdd_adapter *adapter, p2p_peer_authorized(adapter->vdev, sta_mac->bytes); } + hdd_put_sta_info(&adapter->sta_info_list, &sta_info, true); hdd_exit(); return qdf_status; } diff --git a/core/hdd/src/wlan_hdd_sta_info.c b/core/hdd/src/wlan_hdd_sta_info.c index f2e8976f7af0..b6a17b286953 100644 --- a/core/hdd/src/wlan_hdd_sta_info.c +++ b/core/hdd/src/wlan_hdd_sta_info.c @@ -64,6 +64,7 @@ QDF_STATUS hdd_sta_info_attach(struct hdd_sta_info_obj *sta_info_container, qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + qdf_atomic_set(&sta_info->ref_cnt, 1); qdf_ht_add(sta_info_container->sta_obj, &sta_info->sta_node, WLAN_HDD_STA_INFO_HASH(sta_info->sta_mac.bytes)); @@ -83,19 +84,16 @@ void hdd_sta_info_detach(struct hdd_sta_info_obj *sta_info_container, } info = *sta_info; + + if (!info) + return; + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); qdf_ht_remove(&(info->sta_node)); + hdd_put_sta_info(sta_info_container, sta_info, false); qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); - - if (info->assoc_req_ies.len) { - qdf_mem_free(info->assoc_req_ies.data); - info->assoc_req_ies.data = NULL; - info->assoc_req_ies.len = 0; - } - qdf_mem_free(info); - *sta_info = NULL; } struct hdd_station_info *hdd_get_sta_info_by_mac( @@ -115,6 +113,7 @@ struct hdd_station_info *hdd_get_sta_info_by_mac( sta_node, WLAN_HDD_STA_INFO_HASH(mac_addr)) { if (qdf_is_macaddr_equal(&sta_info->sta_mac, (struct qdf_mac_addr *)mac_addr)) { + qdf_atomic_inc(&sta_info->ref_cnt); qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); return sta_info; } @@ -125,6 +124,45 @@ struct hdd_station_info *hdd_get_sta_info_by_mac( return NULL; } +void hdd_put_sta_info(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **sta_info, bool lock_required) +{ + struct hdd_station_info *info; + + if (!sta_info_container || !sta_info) { + hdd_err("Parameter(s) null"); + return; + } + + info = *sta_info; + + if (!info) + return; + + if (lock_required) + qdf_spin_lock_bh(&sta_info_container->sta_obj_lock); + + qdf_atomic_dec(&info->ref_cnt); + + if (qdf_atomic_read(&info->ref_cnt)) { + if (lock_required) + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); + return; + } + + if (info->assoc_req_ies.len) { + qdf_mem_free(info->assoc_req_ies.data); + info->assoc_req_ies.data = NULL; + info->assoc_req_ies.len = 0; + } + + qdf_mem_free(info); + *sta_info = NULL; + + if (lock_required) + qdf_spin_unlock_bh(&sta_info_container->sta_obj_lock); +} + void hdd_clear_cached_sta_info(struct hdd_sta_info_obj *sta_info_container) { struct hdd_station_info *sta_info = NULL; diff --git a/core/hdd/src/wlan_hdd_sta_info.h b/core/hdd/src/wlan_hdd_sta_info.h index 61c72e35967f..ff311b8dce7f 100644 --- a/core/hdd/src/wlan_hdd_sta_info.h +++ b/core/hdd/src/wlan_hdd_sta_info.h @@ -122,6 +122,7 @@ enum dhcp_nego_status { * feature, if it is 0 it indicates FW doesn't support this feature * @sta_info: The sta_info node for the station info list maintained in adapter * @assoc_req_ies: Assoc request IEs of the peer station + * @ref_cnt: Reference count to synchronize sta_info access */ struct hdd_station_info { bool in_use; @@ -171,6 +172,7 @@ struct hdd_station_info { uint32_t rx_mc_bc_cnt; struct qdf_ht_entry sta_node; struct wlan_ies assoc_req_ies; + qdf_atomic_t ref_cnt; }; /** @@ -269,6 +271,18 @@ QDF_STATUS hdd_sta_info_attach(struct hdd_sta_info_obj *sta_info_container, struct hdd_station_info *hdd_get_sta_info_by_mac( struct hdd_sta_info_obj *sta_info_container, const uint8_t *mac_addr); + +/** + * hdd_put_sta_info() - Release sta_info for synchronization + * @sta_info_container: The station info container obj that stores and maintains + * the sta_info obj. + * @sta_info: Station info structure to be released. + * + * Return: None + */ +void hdd_put_sta_info(struct hdd_sta_info_obj *sta_info_container, + struct hdd_station_info **sta_info, bool lock_required); + /** * hdd_clear_cached_sta_info() - Clear the cached sta info from the container * @sta_info_container: The station info container obj that stores and maintains diff --git a/core/hdd/src/wlan_hdd_station_info.c b/core/hdd/src/wlan_hdd_station_info.c index e216f3bdd9e2..c316673abf10 100644 --- a/core/hdd/src/wlan_hdd_station_info.c +++ b/core/hdd/src/wlan_hdd_station_info.c @@ -1059,6 +1059,7 @@ static int hdd_get_cached_station_remote(struct hdd_context *hdd_ctx, skb = cfg80211_vendor_cmd_alloc_reply_skb(hdd_ctx->wiphy, nl_buf_len); if (!skb) { + hdd_put_sta_info(&adapter->cache_sta_info_list, &stainfo, true); hdd_err("cfg80211_vendor_cmd_alloc_reply_skb failed"); return -ENOMEM; } @@ -1125,10 +1126,12 @@ static int hdd_get_cached_station_remote(struct hdd_context *hdd_ctx, } } hdd_sta_info_detach(&adapter->cache_sta_info_list, &stainfo); + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); qdf_atomic_dec(&adapter->cache_sta_count); return cfg80211_vendor_cmd_reply(skb); fail: + hdd_put_sta_info(&adapter->cache_sta_info_list, &stainfo, true); if (skb) kfree_skb(skb); @@ -1286,11 +1289,13 @@ static int hdd_get_station_remote(struct hdd_context *hdd_ctx, if (!is_associated) { status = hdd_get_cached_station_remote(hdd_ctx, adapter, mac_addr); + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); return status; } status = hdd_get_connected_station_info(hdd_ctx, adapter, mac_addr, stainfo); + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); return status; } diff --git a/core/hdd/src/wlan_hdd_stats.c b/core/hdd/src/wlan_hdd_stats.c index 61492fd99a02..5c9825f6994e 100644 --- a/core/hdd/src/wlan_hdd_stats.c +++ b/core/hdd/src/wlan_hdd_stats.c @@ -4083,6 +4083,7 @@ static int wlan_hdd_get_station_remote(struct wiphy *wiphy, qdf_mem_copy(macaddr.bytes, mac, QDF_MAC_ADDR_SIZE); status = wlan_hdd_get_peer_info(adapter, macaddr, &peer_info); if (status) { + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); hdd_err("fail to get peer info from fw"); return -EPERM; } @@ -4097,6 +4098,7 @@ static int wlan_hdd_get_station_remote(struct wiphy *wiphy, txrx_stats.rssi = peer_info.rssi + WLAN_HDD_TGT_NOISE_FLOOR_DBM; wlan_hdd_fill_rate_info(&txrx_stats, &peer_info); wlan_hdd_fill_station_info(hddctx->psoc, sinfo, stainfo, &txrx_stats); + hdd_put_sta_info(&adapter->sta_info_list, &stainfo, true); return status; }