Commit 755b37c9 authored by Brian Norris's avatar Brian Norris Committed by Kalle Valo

mwifiex: catch mwifiex_fw_dpc() errors properly in reset

When resetting the device, we take a synchronous firmware-loading code
path, which borrows a lot from the asynchronous path used at probe time.
We don't catch errors correctly though, which means that in the PCIe
driver, we may try to dereference the 'adapter' struct after
mwifiex_fw_dpc() has freed it. See this (erronous) print in
mwifiex_pcie_reset_notify():

	mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__);

Let's instead refactor the synchronous (or "!req_fw_nowait") path so
that we propagate errors and handle them properly.

This fixes a use-after-free issue in the PCIe driver, as well as a
misleading debug message ("successful"). It looks like the SDIO driver
doesn't have these problems, since it doesn't do anything after
mwifiex_reinit_sw().

Fixes: 4c5dae59 ("mwifiex: add PCIe function level reset support")
Signed-off-by: default avatarBrian Norris <briannorris@chromium.org>
Signed-off-by: default avatarKalle Valo <kvalo@codeaurora.org>
parent ce8fad9a
......@@ -512,7 +512,7 @@ static void mwifiex_terminate_workqueue(struct mwifiex_adapter *adapter)
* - Download the correct firmware to card
* - Issue the init commands to firmware
*/
static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
static int _mwifiex_fw_dpc(const struct firmware *firmware, void *context)
{
int ret;
char fmt[64];
......@@ -665,11 +665,18 @@ static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
mwifiex_free_adapter(adapter);
/* Tell all current and future waiters we're finished */
complete_all(fw_done);
return;
return init_failed ? -EIO : 0;
}
static void mwifiex_fw_dpc(const struct firmware *firmware, void *context)
{
_mwifiex_fw_dpc(firmware, context);
}
/*
* This function initializes the hardware and gets firmware.
* This function gets the firmware and (if called asynchronously) kicks off the
* HW init when done.
*/
static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter,
bool req_fw_nowait)
......@@ -692,20 +699,15 @@ static int mwifiex_init_hw_fw(struct mwifiex_adapter *adapter,
ret = request_firmware_nowait(THIS_MODULE, 1, adapter->fw_name,
adapter->dev, GFP_KERNEL, adapter,
mwifiex_fw_dpc);
if (ret < 0)
mwifiex_dbg(adapter, ERROR,
"request_firmware_nowait error %d\n", ret);
} else {
ret = request_firmware(&adapter->firmware,
adapter->fw_name,
adapter->dev);
if (ret < 0)
mwifiex_dbg(adapter, ERROR,
"request_firmware error %d\n", ret);
else
mwifiex_fw_dpc(adapter->firmware, (void *)adapter);
}
if (ret < 0)
mwifiex_dbg(adapter, ERROR, "request_firmware%s error %d\n",
req_fw_nowait ? "_nowait" : "", ret);
return ret;
}
......@@ -1427,6 +1429,8 @@ EXPORT_SYMBOL_GPL(mwifiex_shutdown_sw);
int
mwifiex_reinit_sw(struct mwifiex_adapter *adapter)
{
int ret;
mwifiex_init_lock_list(adapter);
if (adapter->if_ops.up_dev)
adapter->if_ops.up_dev(adapter);
......@@ -1473,6 +1477,13 @@ mwifiex_reinit_sw(struct mwifiex_adapter *adapter)
"%s: firmware init failed\n", __func__);
goto err_init_fw;
}
/* _mwifiex_fw_dpc() does its own cleanup */
ret = _mwifiex_fw_dpc(adapter->firmware, adapter);
if (ret) {
pr_err("Failed to bring up adapter: %d\n", ret);
return ret;
}
mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__);
return 0;
......
......@@ -350,6 +350,7 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare)
{
struct pcie_service_card *card = pci_get_drvdata(pdev);
struct mwifiex_adapter *adapter = card->adapter;
int ret;
if (!adapter) {
dev_err(&pdev->dev, "%s: adapter structure is not valid\n",
......@@ -376,7 +377,11 @@ static void mwifiex_pcie_reset_notify(struct pci_dev *pdev, bool prepare)
* and firmware including firmware redownload
*/
adapter->surprise_removed = false;
mwifiex_reinit_sw(adapter);
ret = mwifiex_reinit_sw(adapter);
if (ret) {
dev_err(&pdev->dev, "reinit failed: %d\n", ret);
return;
}
}
mwifiex_dbg(adapter, INFO, "%s, successful\n", __func__);
}
......
......@@ -2196,6 +2196,7 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
{
struct sdio_mmc_card *card = adapter->card;
struct sdio_func *func = card->func;
int ret;
mwifiex_shutdown_sw(adapter);
......@@ -2210,7 +2211,9 @@ static void mwifiex_sdio_card_reset_work(struct mwifiex_adapter *adapter)
clear_bit(MWIFIEX_IFACE_WORK_DEVICE_DUMP, &card->work_flags);
clear_bit(MWIFIEX_IFACE_WORK_CARD_RESET, &card->work_flags);
mwifiex_reinit_sw(adapter);
ret = mwifiex_reinit_sw(adapter);
if (ret)
dev_err(&func->dev, "reinit failed: %d\n", ret);
}
/* This function read/write firmware */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment