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
This commit is contained in:
Bapiraju Alla 2020-03-17 13:35:39 +05:30 • committed by nshrivas
commit 51ab1164a1
7 changed files with 98 additions and 12 deletions

View file

@ -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:

View file

@ -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;

View file

@ -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;
}

View file

@ -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;

View file

@ -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

View file

@ -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;
}

View file

@ -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;
}