[PATCH] rtl8712: fix race between firmware failing to load and disconnect
From: Oliver Neukum <oneukum@suse.com>
Date: 2019-11-14 09:42:35
Subsystem:
staging subsystem, the rest · Maintainers:
Greg Kroah-Hartman, Linus Torvalds
If a device is unplugged before the firmware is loaded the disconnect() has to wait for the firmware load to finish. The driver does that, but it does so in a racy manner. There are two races here. First, the wait is ended before the net device is registered, which leads to a race between register and deregister. Second, the wait is eneded before errors in the firmware load are handled and the error is ignored during disconnect. The fix is to do the complete() at the end of the handler for firmware load. That requires splitting it for the normal and the error code path. Furthermore the result of the firmware load needs to be checked in the disconnect() method. This fix was tested by syzbot, but it reported a different crash, so I cannot claim a successful testing. Reported-by: syzbot+80899a8a8efe8968cde7@syzkaller.appspotmail.com Signed-off-by: Oliver Neukum <oneukum@suse.com> --- drivers/staging/rtl8712/hal_init.c | 3 ++- drivers/staging/rtl8712/usb_intf.c | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/drivers/staging/rtl8712/hal_init.c b/drivers/staging/rtl8712/hal_init.c
index 40145c0338e4..42c0a3c947f1 100644
--- a/drivers/staging/rtl8712/hal_init.c
+++ b/drivers/staging/rtl8712/hal_init.c@@ -33,7 +33,6 @@ static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context) { struct _adapter *adapter = context; - complete(&adapter->rtl8712_fw_ready); if (!firmware) { struct usb_device *udev = adapter->dvobjpriv.pusbdev; struct usb_interface *usb_intf = adapter->pusb_intf;
@@ -41,11 +40,13 @@ static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context) dev_err(&udev->dev, "r8712u: Firmware request failed\n"); usb_put_dev(udev); usb_set_intfdata(usb_intf, NULL); + complete(&adapter->rtl8712_fw_ready); return; } adapter->fw = firmware; /* firmware available - start netdev */ register_netdev(adapter->pnetdev); + complete(&adapter->rtl8712_fw_ready); } static const char firmware_file[] = "rtlwifi/rtl8712u.bin";
diff --git a/drivers/staging/rtl8712/usb_intf.c b/drivers/staging/rtl8712/usb_intf.c
index ba1288297ee4..e6137603309c 100644
--- a/drivers/staging/rtl8712/usb_intf.c
+++ b/drivers/staging/rtl8712/usb_intf.c@@ -595,10 +595,13 @@ static void r871xu_dev_remove(struct usb_interface *pusb_intf) if (pnetdev) { struct _adapter *padapter = netdev_priv(pnetdev); - usb_set_intfdata(pusb_intf, NULL); - release_firmware(padapter->fw); /* never exit with a firmware callback pending */ wait_for_completion(&padapter->rtl8712_fw_ready); + pnetdev = usb_get_intfdata(pusb_intf); + usb_set_intfdata(pusb_intf, NULL); + if (!pnetdev) + goto raced_with_firmware_failure; + release_firmware(padapter->fw); if (drvpriv.drv_registered) padapter->surprise_removed = true; unregister_netdev(pnetdev); /* will call netdev_close() */
@@ -609,6 +612,7 @@ static void r871xu_dev_remove(struct usb_interface *pusb_intf) r871x_dev_unload(padapter); r8712_free_drv_sw(padapter); +raced_with_firmware_failure: /* decrease the reference count of the usb device structure * when disconnect */
--
2.16.4