Эх сурвалжийг харах

qcacmn: Fix use after free for vdev in tdls

When disconnect happens, TDLS_NOTIFY_STA_DISCONNECTION will be
send to scheduler_thread, in bad case, it is processed after
3s later, and the vdev passed in notify msg has been freed in
the period, invalid vdev causes invalid memory access.

change as below:
 1.1 Get vdev ref before send msg
 1.2 register callback for release ref
 1.3 send msg to scheduler_thread
 1.4 process tdls notify msg and call callback function.

Change-Id: I2b03db8b30db623796f2e8299f14ee31e28efb91
CRs-Fixed: 2275019
Jingxiang Ge 6 жил өмнө
parent
commit
2b64e4d5ff

+ 58 - 5
os_if/linux/tdls/src/wlan_cfg80211_tdls.c

@@ -128,20 +128,55 @@ void hdd_notify_tdls_reset_adapter(struct wlan_objmgr_vdev *vdev)
 	ucfg_tdls_notify_reset_adapter(vdev);
 }
 
+static void hdd_notify_sta_connect_callback(struct wlan_objmgr_vdev *vdev)
+{
+	if (!vdev) {
+		cfg80211_err("vdev is NULL");
+		return;
+	}
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+}
+
 void
 hdd_notify_sta_connect(uint8_t session_id,
 		       bool tdls_chan_swit_prohibited,
 		       bool tdls_prohibited,
 		       struct wlan_objmgr_vdev *vdev)
 {
-	struct tdls_sta_notify_params notify_info;
+	struct tdls_sta_notify_params notify_info = {0};
+	QDF_STATUS status;
+
+	if (!vdev) {
+		cfg80211_err("vdev is NULL");
+		return;
+	}
+	status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cfg80211_err("can't get vdev");
+		return;
+	}
 
 	notify_info.session_id = session_id;
 	notify_info.vdev = vdev;
 	notify_info.tdls_chan_swit_prohibited = tdls_chan_swit_prohibited;
 	notify_info.tdls_prohibited = tdls_prohibited;
-	ucfg_tdls_notify_sta_connect(&notify_info);
+	notify_info.callback = hdd_notify_sta_connect_callback;
+	status = ucfg_tdls_notify_sta_connect(&notify_info);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cfg80211_err("ucfg_tdls_notify_sta_connect failed");
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+	}
+}
 
+static void hdd_notify_sta_disconnect_callback(struct wlan_objmgr_vdev *vdev)
+{
+	if (!vdev) {
+		cfg80211_err("vdev is NULL");
+		return;
+	}
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
 }
 
 void hdd_notify_sta_disconnect(uint8_t session_id,
@@ -149,14 +184,32 @@ void hdd_notify_sta_disconnect(uint8_t session_id,
 			       bool user_disconnect,
 			       struct wlan_objmgr_vdev *vdev)
 {
-	struct tdls_sta_notify_params notify_info;
+	struct tdls_sta_notify_params notify_info = {0};
+	QDF_STATUS status;
+
+	if (!vdev) {
+		cfg80211_err("vdev is NULL");
+		return;
+	}
+
+	status = wlan_objmgr_vdev_try_get_ref(vdev, WLAN_TDLS_NB_ID);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cfg80211_err("can't get vdev");
+		return;
+	}
 
 	notify_info.session_id = session_id;
 	notify_info.lfr_roam = lfr_roam;
+	notify_info.tdls_chan_swit_prohibited = false;
+	notify_info.tdls_prohibited = false;
 	notify_info.vdev = vdev;
 	notify_info.user_disconnect = user_disconnect;
-	ucfg_tdls_notify_sta_disconnect(&notify_info);
-
+	notify_info.callback = hdd_notify_sta_disconnect_callback;
+	status = ucfg_tdls_notify_sta_disconnect(&notify_info);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		cfg80211_err("ucfg_tdls_notify_sta_disconnect failed");
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
+	}
 }
 int wlan_cfg80211_tdls_add_peer(struct wlan_objmgr_pdev *pdev,
 				struct net_device *dev, const uint8_t *mac)

+ 16 - 25
umac/tdls/core/src/wlan_tdls_main.c

@@ -901,19 +901,16 @@ QDF_STATUS tdls_notify_sta_connect(struct tdls_sta_notify_params *notify)
 {
 	QDF_STATUS status;
 
-	if (!notify || !notify->vdev)
-		return QDF_STATUS_E_INVAL;
-
-	if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(notify->vdev,
-							WLAN_TDLS_NB_ID)) {
-		qdf_mem_free(notify);
+	if (!notify || !notify->vdev) {
+		tdls_err("invalid param");
 		return QDF_STATUS_E_INVAL;
 	}
 
 	status = tdls_process_sta_connect(notify);
 
-	wlan_objmgr_vdev_release_ref(notify->vdev,
-				     WLAN_TDLS_NB_ID);
+	if (notify->callback)
+		notify->callback(notify->vdev);
+
 	qdf_mem_free(notify);
 	return status;
 }
@@ -993,19 +990,15 @@ QDF_STATUS tdls_notify_sta_disconnect(struct tdls_sta_notify_params *notify)
 {
 	QDF_STATUS status;
 
-	if (!notify || !notify->vdev)
-		return QDF_STATUS_E_INVAL;
-
-	if (QDF_STATUS_SUCCESS != wlan_objmgr_vdev_try_get_ref(notify->vdev,
-							WLAN_TDLS_NB_ID)) {
-		qdf_mem_free(notify);
+	if (!notify || !notify->vdev) {
+		tdls_err("invalid param");
 		return QDF_STATUS_E_INVAL;
 	}
 
 	status = tdls_process_sta_disconnect(notify);
 
-	wlan_objmgr_vdev_release_ref(notify->vdev,
-			     WLAN_TDLS_NB_ID);
+	if (notify->callback)
+		notify->callback(notify->vdev);
 
 	qdf_mem_free(notify);
 	return status;
@@ -1127,8 +1120,8 @@ void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev)
 	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_NB_ID);
 }
 
-void tdls_peers_deleted_notification(struct wlan_objmgr_vdev *vdev,
-					 uint32_t session_id)
+QDF_STATUS tdls_peers_deleted_notification(
+				struct tdls_sta_notify_params *notify_info)
 {
 	struct scheduler_msg msg = {0, };
 	struct tdls_sta_notify_params *notify;
@@ -1137,15 +1130,10 @@ void tdls_peers_deleted_notification(struct wlan_objmgr_vdev *vdev,
 	notify = qdf_mem_malloc(sizeof(*notify));
 	if (!notify) {
 		tdls_err("memory allocation failed !!!");
-		return;
+		return QDF_STATUS_E_NULL_VALUE;
 	}
 
-	notify->lfr_roam = true;
-	notify->tdls_chan_swit_prohibited = false;
-	notify->tdls_prohibited = false;
-	notify->session_id = session_id;
-	notify->vdev = vdev;
-	notify->user_disconnect = false;
+	*notify = *notify_info;
 
 	msg.bodyptr = notify;
 	msg.callback = tdls_process_cmd;
@@ -1155,7 +1143,10 @@ void tdls_peers_deleted_notification(struct wlan_objmgr_vdev *vdev,
 	if (QDF_IS_STATUS_ERROR(status)) {
 		qdf_mem_free(notify);
 		tdls_alert("message post failed ");
+		return QDF_STATUS_E_FAILURE;
 	}
+
+	return QDF_STATUS_SUCCESS;
 }
 
 /**

+ 4 - 5
umac/tdls/core/src/wlan_tdls_main.h

@@ -584,17 +584,16 @@ void tdls_notify_reset_adapter(struct wlan_objmgr_vdev *vdev);
 
 /**
  * tdls_peers_deleted_notification() - peer delete notification
- * @vdev: vdev object
- * @session_id: session id
+ * @notify_info: peer info
  *
  * Legacy lim layer will delete tdls peers for roaming and heart beat failures
  * and notify the component about the delete event to update the tdls.
  * state.
  *
- * Return: None
+ * Return: QDF_STATUS
  */
-void tdls_peers_deleted_notification(struct wlan_objmgr_vdev *vdev,
-					 uint32_t session_id);
+QDF_STATUS tdls_peers_deleted_notification(
+		struct tdls_sta_notify_params *notify_info);
 
 /**
  * tdls_notify_decrement_session() - Notify the session decrement

+ 4 - 0
umac/tdls/dispatcher/inc/wlan_tdls_public_structs.h

@@ -563,6 +563,9 @@ typedef QDF_STATUS
 				      uint32_t vdev_param,
 				      bool is_link_up);
 
+/* This callback is to release vdev ref for tdls sta notify msg */
+typedef void (*tdls_sta_notify_callback)(struct wlan_objmgr_vdev *vdev);
+
 /**
  * struct tdls_start_params - tdls start params
  * @config: tdls user config
@@ -1041,6 +1044,7 @@ struct tdls_sta_notify_params {
 	bool lfr_roam;
 	bool user_disconnect;
 	uint8_t session_id;
+	tdls_sta_notify_callback callback;
 };
 
 /**

+ 25 - 4
umac/tdls/dispatcher/src/wlan_tdls_tgt_api.c

@@ -329,10 +329,23 @@ release_nbuf:
 	return status;
 }
 
+static void tgt_tdls_peers_deleted_notification_callback(
+			struct wlan_objmgr_vdev *vdev)
+{
+	if (!vdev) {
+		tdls_err("vdev is NULL");
+		return;
+	}
+
+	wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+}
+
 void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc,
 						uint32_t session_id)
 {
 	struct wlan_objmgr_vdev *vdev;
+	struct tdls_sta_notify_params notify_info;
+	QDF_STATUS status;
 
 	vdev = wlan_objmgr_get_vdev_by_id_from_psoc(psoc,
 						    session_id,
@@ -344,9 +357,17 @@ void tgt_tdls_peers_deleted_notification(struct wlan_objmgr_psoc *psoc,
 		return;
 	}
 
-	tdls_peers_deleted_notification(vdev, session_id);
-
-	wlan_objmgr_vdev_release_ref(vdev,
-				     WLAN_TDLS_SB_ID);
+	notify_info.lfr_roam = true;
+	notify_info.tdls_chan_swit_prohibited = false;
+	notify_info.tdls_prohibited = false;
+	notify_info.session_id = session_id;
+	notify_info.vdev = vdev;
+	notify_info.user_disconnect = false;
+	notify_info.callback = tgt_tdls_peers_deleted_notification_callback;
+	status = tdls_peers_deleted_notification(&notify_info);
+	if (QDF_IS_STATUS_ERROR(status)) {
+		tdls_err("tdls_peers_deleted_notification failed");
+		wlan_objmgr_vdev_release_ref(vdev, WLAN_TDLS_SB_ID);
+	}
 }
 

+ 2 - 11
umac/tdls/dispatcher/src/wlan_tdls_ucfg_api.c

@@ -627,11 +627,7 @@ QDF_STATUS ucfg_tdls_notify_sta_connect(
 	if (!notify)
 		return QDF_STATUS_E_NULL_VALUE;
 
-	notify->session_id = notify_info->session_id;
-	notify->tdls_chan_swit_prohibited =
-			notify_info->tdls_chan_swit_prohibited;
-	notify->tdls_prohibited = notify_info->tdls_prohibited;
-	notify->vdev = notify_info->vdev;
+	*notify = *notify_info;
 
 	msg.bodyptr = notify;
 	msg.callback = tdls_process_cmd;
@@ -659,12 +655,7 @@ QDF_STATUS ucfg_tdls_notify_sta_disconnect(
 	if (!notify)
 		return QDF_STATUS_E_NULL_VALUE;
 
-	notify->session_id = notify_info->session_id;
-	notify->tdls_chan_swit_prohibited = false;
-	notify->tdls_prohibited = false;
-	notify->vdev = notify_info->vdev;
-	notify->lfr_roam = notify_info->lfr_roam;
-	notify->user_disconnect = notify_info->user_disconnect;
+	*notify = *notify_info;
 
 	msg.bodyptr = notify;
 	msg.callback = tdls_process_cmd;