Jelajahi Sumber

qcacld-3.0: Correct the condition to reject TDLS

Reject any incoming get_all_peers TDLS command if there
are any currently in progress.
The later commands will only be processed once the earlier
commands finish, since they are processed sequentially.
However, once a command finishes, the memory allocated for
it from userspace is freed up. Each command is passed the
same address so the later commands will end up writing to
a freed address. To avoid this, reject incoming requests
if there are any still in progress.
For this, the condition should be corrected.
We shouldn't use completion_done here for checking for
completion as this will always return false, as
tdls_user_cmd_comp.done will remain in init state always.
So, the very first command will also not work.
In general completion_done is used to check if there are
multiple threads waiting on the complete event that's
why it will return true only when tdls_user_cmd_comp.done
is set with complete().
Also, if the state is in wait_for_completion, this function
will return true after the wait timer is over or condition
is met as wait_for_completion will hold out the hold lock
and will will prevent completion_done from returning.
Better to use a flag to determine command condition.

Change-Id: I1b6b270dbb9b0b103f10e7ae22a60030ea2fbb98
CRs-Fixed: 3162184
Utkarsh Bhatnagar 3 tahun lalu
induk
melakukan
8af8418afd
2 mengubah file dengan 23 tambahan dan 1 penghapusan
  1. 3 0
      os_if/tdls/inc/wlan_cfg80211_tdls.h
  2. 20 1
      os_if/tdls/src/wlan_cfg80211_tdls.c

+ 3 - 0
os_if/tdls/inc/wlan_cfg80211_tdls.h

@@ -1,5 +1,6 @@
 /*
  * Copyright (c) 2017-2020 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
  *
  * Permission to use, copy, modify, and/or distribute this software for
  * any purpose with or without fee is hereby granted, provided that the
@@ -51,6 +52,7 @@
  * @mgmt_tx_completion_status: Tdls mgmt frames TX completion status code
  * @tdls_user_cmd_len: tdls user command written buffer length
  * @tdls_antenna_switch_status: return status after antenna switch
+ * @tdls_user_cmd_in_progress: tdls user command progress status.
  */
 struct osif_tdls_vdev {
 	struct completion tdls_add_peer_comp;
@@ -64,6 +66,7 @@ struct osif_tdls_vdev {
 	uint32_t mgmt_tx_completion_status;
 	uint32_t tdls_user_cmd_len;
 	int tdls_antenna_switch_status;
+	bool tdls_user_cmd_in_progress;
 };
 
 /**

+ 20 - 1
os_if/tdls/src/wlan_cfg80211_tdls.c

@@ -680,11 +680,29 @@ int wlan_cfg80211_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev,
 
 	tdls_priv = osif_priv->osif_tdls;
 
-	if (!completion_done(&tdls_priv->tdls_user_cmd_comp)) {
+	/*
+	 * We shouldn't use completion_done here for checking for completion
+	 * as this will always return false, as tdls_user_cmd_comp.done will
+	 * remain in init state always. So, the very first command will also
+	 * not work.
+	 * In general completion_done is used to check if there are multiple
+	 * threads waiting on the complete event that's why it will return true
+	 * only when tdls_user_cmd_comp.done is set with complete()
+	 * In general completion_done will return true only when
+	 * tdls_user_cmd_comp.done is set that will happen in complete().
+	 * Also, if there is already a thread waiting for wait_for_completion,
+	 * this function will
+	 * return true only after the wait timer is over or condition is
+	 * met as wait_for_completion will hold out the hold lock and will
+	 * will prevent completion_done from returning.
+	 * Better to use a flag to determine command condition.
+	 */
+	if (tdls_priv->tdls_user_cmd_in_progress) {
 		osif_err("TDLS user cmd still in progress, reject this one");
 		return -EBUSY;
 	}
 
+	tdls_priv->tdls_user_cmd_in_progress = true;
 	wlan_cfg80211_update_tdls_peers_rssi(vdev);
 
 	reinit_completion(&tdls_priv->tdls_user_cmd_comp);
@@ -714,6 +732,7 @@ int wlan_cfg80211_tdls_get_all_peers(struct wlan_objmgr_vdev *vdev,
 	len = tdls_priv->tdls_user_cmd_len;
 
 error_get_tdls_peers:
+	tdls_priv->tdls_user_cmd_in_progress = false;
 	return len;
 }