qcacld-3.0: Do objmgr vdev destroy before sme close session

Converged host driver code listens for a vdev_destroy event from
object manager to release vdev related resources. Currently, vdevs
are destroyed in firmware before they are destroyed by object manager,
leading to a possible use-after-free situation in firmware. Reverse
the order of vdev destroy, first in object manger, then in firmware,
to prevent the potential use-after-free.

Change-Id: I54bbede9732cecb3bec291692f452758976184c4
CRs-Fixed: 2024633
This commit is contained in:
Dustin Brown 2017-03-24 15:21:32 -07:00 • committed by Sandeep Puligilla
commit 0d2eeaeb87
5 changed files with 80 additions and 84 deletions

View file

@ -6070,8 +6070,9 @@ error_wmm_init:
hdd_softap_deinit_tx_rx(pAdapter);
error_init_ap_mode:
QDF_BUG(!hdd_objmgr_release_and_destroy_vdev(pAdapter));
QDF_BUG(!hdd_objmgr_destroy_vdev(pAdapter));
wlansap_close(sapContext);
QDF_BUG(!hdd_objmgr_release_vdev(pAdapter));
pAdapter->sessionCtx.ap.sapContext = NULL;
EXIT();
@ -6241,14 +6242,18 @@ QDF_STATUS hdd_unregister_hostapd(hdd_adapter_t *pAdapter, bool rtnl_held)
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("Failed:wlansap_stop");
ret = hdd_objmgr_destroy_vdev(pAdapter);
if (ret)
hdd_err("objmgr vdev destroy failed");
status = wlansap_close(sapContext);
if (!QDF_IS_STATUS_SUCCESS(status))
hdd_err("Failed:WLANSAP_close");
pAdapter->sessionCtx.ap.sapContext = NULL;
ret = hdd_objmgr_release_and_destroy_vdev(pAdapter);
ret = hdd_objmgr_release_vdev(pAdapter);
if (ret)
hdd_err("vdev delete failed");
hdd_err("objmgr vdev release failed");
EXIT();
return 0;

View file

@ -2915,8 +2915,8 @@ int hdd_vdev_destroy(hdd_adapter_t *adapter)
return -EINVAL;
}
/* do vdev create via objmgr */
errno = hdd_objmgr_release_and_destroy_vdev(adapter);
/* do vdev logical destroy via objmgr */
errno = hdd_objmgr_destroy_vdev(adapter);
if (errno) {
hdd_err("failed to destroy objmgr vdev: %d", errno);
return errno;
@ -2944,6 +2944,13 @@ int hdd_vdev_destroy(hdd_adapter_t *adapter)
return -ETIMEDOUT;
}
/* now that sme session is closed, allow physical vdev destroy */
errno = hdd_objmgr_release_vdev(adapter);
if (errno) {
hdd_err("failed to release objmgr vdev: %d", errno);
return errno;
}
hdd_info("vdev destroyed successfully");
return 0;
@ -3239,7 +3246,6 @@ void hdd_deinit_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
static void hdd_cleanup_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
bool rtnl_held)
{
int ret;
struct net_device *pWlanDev = NULL;
if (adapter)
@ -3249,10 +3255,6 @@ static void hdd_cleanup_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
return;
}
ret = hdd_objmgr_release_and_destroy_vdev(adapter);
if (ret)
hdd_err("vdev delete failed");
hdd_debugfs_exit(adapter);
if (adapter->scan_info.default_scan_ies) {
@ -3851,54 +3853,6 @@ void wlan_hdd_reset_prob_rspies(hdd_adapter_t *pHostapdAdapter)
}
}
/**
* hdd_wait_for_sme_close_sesion() - Close and wait for SME session close
* @hdd_ctx: HDD context which is already NULL validated
* @adapter: HDD adapter which is already NULL validated
*
* Close the SME session and wait for its completion, if needed.
*
* Return: None
*/
static void hdd_wait_for_sme_close_sesion(hdd_context_t *hdd_ctx,
hdd_adapter_t *adapter)
{
int ret;
unsigned long rc;
if (!test_bit(SME_SESSION_OPENED, &adapter->event_flags)) {
hdd_err("session is not opened:%d", adapter->sessionId);
return;
}
INIT_COMPLETION(adapter->session_close_comp_var);
if (QDF_STATUS_SUCCESS ==
sme_close_session(hdd_ctx->hHal, adapter->sessionId,
hdd_sme_close_session_callback,
adapter)) {
/*
* Block on a completion variable. Can't wait
* forever though.
*/
rc = wait_for_completion_timeout(
&adapter->session_close_comp_var,
msecs_to_jiffies
(WLAN_WAIT_TIME_SESSIONOPENCLOSE));
if (!rc) {
hdd_err("failure waiting for session_close_comp_var");
if (adapter->device_mode == QDF_NDI_MODE)
hdd_ndp_session_end_handler(adapter);
clear_bit(SME_SESSION_OPENED, &adapter->event_flags);
return;
}
ret = hdd_objmgr_release_and_destroy_vdev(adapter);
if (ret)
hdd_err("vdev delete failed");
adapter->sessionId = HDD_SESSION_ID_INVALID;
}
}
QDF_STATUS hdd_stop_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
const bool bCloseSession)
{
@ -3992,7 +3946,7 @@ QDF_STATUS hdd_stop_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
hdd_err("Error: Can't disconnect adapter");
return QDF_STATUS_E_FAILURE;
}
hdd_wait_for_sme_close_sesion(hdd_ctx, adapter);
hdd_vdev_destroy(adapter);
}
break;
@ -4071,7 +4025,7 @@ QDF_STATUS hdd_stop_adapter(hdd_context_t *hdd_ctx, hdd_adapter_t *adapter,
adapter->sessionCtx.ap.beacon = NULL;
}
if (true == bCloseSession)
hdd_wait_for_sme_close_sesion(hdd_ctx, adapter);
hdd_vdev_destroy(adapter);
mutex_unlock(&hdd_ctx->sap_lock);
break;
case QDF_OCB_MODE:

View file

@ -113,9 +113,8 @@ void hdd_nan_datapath_target_config(hdd_context_t *hdd_ctx,
*/
static int hdd_close_ndi(hdd_adapter_t *adapter)
{
int rc;
int errno;
hdd_context_t *hdd_ctx = WLAN_HDD_GET_CTX(adapter);
uint32_t timeout = WLAN_WAIT_TIME_SESSIONOPENCLOSE;
ENTER();
@ -138,24 +137,9 @@ static int hdd_close_ndi(hdd_adapter_t *adapter)
cancel_work_sync(&adapter->ipv6NotifierWorkQueue);
#endif
#endif
/* check if the session is open */
if (test_bit(SME_SESSION_OPENED, &adapter->event_flags)) {
INIT_COMPLETION(adapter->session_close_comp_var);
if (QDF_STATUS_SUCCESS == sme_close_session(hdd_ctx->hHal,
adapter->sessionId,
hdd_sme_close_session_callback, adapter)) {
/* Block on a timed completion variable */
rc = wait_for_completion_timeout(
&adapter->session_close_comp_var,
msecs_to_jiffies(timeout));
if (!rc)
hdd_err("session close timeout");
rc = hdd_objmgr_release_and_destroy_vdev(adapter);
if (rc)
hdd_err("vdev delete failed");
}
}
errno = hdd_vdev_destroy(adapter);
if (errno)
hdd_err("failed to destroy vdev: %d", errno);
/* We are good to close the adapter */
hdd_close_adapter(hdd_ctx, adapter, true);

View file

@ -174,13 +174,20 @@ int hdd_objmgr_create_and_store_vdev(struct wlan_objmgr_pdev *pdev,
return -ENOMEM;
}
/*
* To enable legacy use cases, we need to delay physical vdev destroy
* until after the sme session has been closed. We accomplish this by
* getting an additional reference here.
*/
wlan_objmgr_vdev_get_ref(vdev, WLAN_HDD_ID_OBJ_MGR);
adapter->hdd_vdev = vdev;
adapter->sessionId = wlan_vdev_get_id(vdev);
return 0;
}
int hdd_objmgr_release_and_destroy_vdev(hdd_adapter_t *adapter)
int hdd_objmgr_destroy_vdev(hdd_adapter_t *adapter)
{
struct wlan_objmgr_vdev *vdev;
struct vdev_osif_priv *osif_priv;
@ -193,8 +200,6 @@ int hdd_objmgr_release_and_destroy_vdev(hdd_adapter_t *adapter)
vdev->vdev_nif.osdev = NULL;
qdf_mem_free(osif_priv);
adapter->hdd_vdev = NULL;
adapter->sessionId = HDD_SESSION_ID_INVALID;
if (hdd_objmgr_remove_peer_object(vdev,
wlan_vdev_mlme_get_macaddr(vdev))) {
hdd_err("Self peer delete failed");
@ -204,6 +209,28 @@ int hdd_objmgr_release_and_destroy_vdev(hdd_adapter_t *adapter)
return qdf_status_to_os_return(wlan_objmgr_vdev_obj_delete(vdev));
}
int hdd_objmgr_release_vdev(hdd_adapter_t *adapter)
{
/* allow physical vdev destroy by releasing the hdd reference */
wlan_objmgr_vdev_release_ref(adapter->hdd_vdev, WLAN_HDD_ID_OBJ_MGR);
adapter->hdd_vdev = NULL;
adapter->sessionId = HDD_SESSION_ID_INVALID;
return 0;
}
int hdd_objmgr_release_and_destroy_vdev(hdd_adapter_t *adapter)
{
int errno;
errno = hdd_objmgr_destroy_vdev(adapter);
if (errno)
return errno;
return hdd_objmgr_release_vdev(adapter);
}
int hdd_objmgr_add_peer_object(struct wlan_objmgr_vdev *vdev,
enum tQDF_ADAPTER_MODE adapter_mode,
uint8_t *mac_addr)

View file

@ -118,9 +118,35 @@ int hdd_objmgr_release_and_destroy_pdev(hdd_context_t *hdd_ctx);
int hdd_objmgr_create_and_store_vdev(struct wlan_objmgr_pdev *pdev,
hdd_adapter_t *adapter);
/**
* hdd_objmgr_destroy_vdev() - Delete vdev
* @adapter: hdd adapter
*
* This function logically destroys the vdev in object manager. Physical
* deletion is prevented until the vdev is released via a call to
* hdd_objmgr_release_vdev(). E.g.
*
* hdd_objmgr_destroy_vdev(...);
* sme_close_session(...);
* hdd_objmgr_release_vdev(...);
*
* Return: 0 for success, negative error code for failure
*/
int hdd_objmgr_destroy_vdev(hdd_adapter_t *adapter);
/**
* hdd_objmgr_release_vdev() - releases the vdev from adapter
* @adapter: hdd adapter
*
* See also hdd_objmgr_destroy_vdev()
*
* Return: 0 for success, negative error code for failure
*/
int hdd_objmgr_release_vdev(hdd_adapter_t *adapter);
/**
* hdd_objmgr_release_and_destroy_vdev() - Delete vdev and remove from adapter
* @hdd_ctx: Hdd context
* @adapter: hdd adapter
*
* This API deletes vdev object and release its reference from hdd adapter
*