Преглед изворни кода

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
Bapiraju Alla пре 5 година
родитељ
комит
51ab1164a1

+ 4 - 0
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:

+ 15 - 3
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;

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

+ 46 - 8
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;

+ 14 - 0
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

+ 5 - 0
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;
 }
 

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