mwifiex: catch mwifiex_fw_dpc() errors properly in reset
authorBrian Norris <briannorris@chromium.org>
Tue, 28 Mar 2017 23:59:33 +0000 (16:59 -0700)
committerKalle Valo <kvalo@codeaurora.org>
Wed, 5 Apr 2017 12:44:04 +0000 (15:44 +0300)
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: 4c5dae59d2e9 ("mwifiex: add PCIe function level reset support")
Signed-off-by: Brian Norris <briannorris@chromium.org>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
drivers/net/wireless/marvell/mwifiex/main.c
drivers/net/wireless/marvell/mwifiex/pcie.c
drivers/net/wireless/marvell/mwifiex/sdio.c

index 98c83453ba5be99e0fa7fd42b874d66b699e1720..0dfbac8506894cf71830b6c64b105a484ceb3ec7 100644 (file)
@@ -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 @@ done:
                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;
index e381deff37f3a8b518336c391f64d78e90150981..f45ab125cd0d07c8ea170dfd2cb3f62b0ff9205c 100644 (file)
@@ -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__);
 }
index 58d3da09cfbd0bb33a4eeb142f35e3b487536979..424532b81c2de5d3565467f269ec1dd7298025b6 100644 (file)
@@ -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 */