Skip to contentSkip to conversation

staging: rtl8712: fix error handling

PATCH New Pavel Skripkin 1 participant 3 messages Jul 21, 2021 Staging

lore
Share & tools
lore
#0 lore raw

cover staging: rtl8712: fix error handling

Hi, rtl8712 developers and stanging maintainers!

In this patch series I rewrote error handling approach in rtl8712 driver. Detailed description can be found in commit messages. In short:

There was strage approach to handle fw load error. For some reason fw callback was doing clean up stuff which can lead to UAF bug. For example:

CPU0 CPU1
r871xu_dev_remove()

                                          rtl871x_load_fw_cb()
                                          free_netdev(netdev)

wait_for_completion(netdev_priv->compl) <- UAF, slab-out-of-bound or smth else

I've added free_netdev() call in my previous patch to this driver:
e02a3b945816 ("staging: rtl8712: fix memory leak in rtl871x_load_fw_cb") to avoid memory leak and I believed, that this approach won't trigger anything else, but, unfortunately, I was wrong. Syzbot found 2 bugs [1] [2] and I decided to complely rewrite error handling in case of fw load failure. This patch series was tested with both reproducers and did't trigger any bugs.

[1] https://syzkaller.appspot.com/bug?id=7646834b55c71c45ed85f601032daa6c23db0513
[2] https://syzkaller.appspot.com/bug?id=89c3ddb9936d3552995130298f1d2633ab9d3541

With regards,
Pavel Skripkin

Pavel Skripkin (2):
staging: rtl8712: get rid of flush_scheduled_work
staging: rtl8712: error handling refactoring

 drivers/staging/rtl8712/hal_init.c        | 30 ++++++++-----
 drivers/staging/rtl8712/rtl8712_led.c     |  8 ++++
 drivers/staging/rtl8712/rtl871x_led.h     |  1 +
 drivers/staging/rtl8712/rtl871x_pwrctrl.c |  8 ++++
 drivers/staging/rtl8712/rtl871x_pwrctrl.h |  1 +
 drivers/staging/rtl8712/usb_intf.c        | 51 ++++++++++-------------
 6 files changed, 61 insertions(+), 38 deletions(-)
Signature · 1 line
2.32.0
#1 lore raw

PATCH 1/2 staging: rtl8712: get rid of flush_scheduled_work

This patch is preparation for following patch for error handling refactoring.

flush_scheduled_work() takes (wq_completion)events lock and
it can lead to deadlock when r871xu_dev_remove() is called from workqueue. To avoid deadlock sutiation we can change flush_scheduled_work() call to flush_work() call for all possibly scheduled works in this driver, since next patch adds device_release_driver() in case of fw load failure.

---
 drivers/staging/rtl8712/rtl8712_led.c     | 8 ++++++++
 drivers/staging/rtl8712/rtl871x_led.h     | 1 +
 drivers/staging/rtl8712/rtl871x_pwrctrl.c | 8 ++++++++
 drivers/staging/rtl8712/rtl871x_pwrctrl.h | 1 +
 drivers/staging/rtl8712/usb_intf.c        | 3 ++-
 5 files changed, 20 insertions(+), 1 deletion(-)
drivers/staging/rtl8712/rtl8712_led.c +8 -0
index 5901026949f2..d5fc9026b036 100644
@@ -1820,3 +1820,11 @@ void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction)
 		break;
 	}
 }
+
+void r8712_flush_led_works(struct _adapter *padapter)
+{
+	struct led_priv *pledpriv = &padapter->ledpriv;
+
+	flush_work(&pledpriv->SwLed0.BlinkWorkItem);
+	flush_work(&pledpriv->SwLed1.BlinkWorkItem);
+}
drivers/staging/rtl8712/rtl871x_led.h +1 -0
index ee19c873cf01..2f0768132ad8 100644
@@ -112,6 +112,7 @@ struct led_priv {
 void r8712_InitSwLeds(struct _adapter *padapter);
 void r8712_DeInitSwLeds(struct _adapter *padapter);
 void LedControl871x(struct _adapter *padapter, enum LED_CTL_MODE LedAction);
+void r8712_flush_led_works(struct _adapter *padapter);
 
 #endif
 
drivers/staging/rtl8712/rtl871x_pwrctrl.c +8 -0
index 23cff43437e2..cd6d9ff0bebc 100644
@@ -224,3 +224,11 @@ void r8712_unregister_cmd_alive(struct _adapter *padapter)
 	}
 	mutex_unlock(&pwrctrl->mutex_lock);
 }
+
+void r8712_flush_rwctrl_works(struct _adapter *padapter)
+{
+	struct pwrctrl_priv *pwrctrl = &padapter->pwrctrlpriv;
+
+	flush_work(&pwrctrl->SetPSModeWorkItem);
+	flush_work(&pwrctrl->rpwm_workitem);
+}
drivers/staging/rtl8712/rtl871x_pwrctrl.h +1 -0
index bf6623cfaf27..b35b9c7920eb 100644
@@ -108,5 +108,6 @@ void r8712_cpwm_int_hdl(struct _adapter *padapter,
 void r8712_set_ps_mode(struct _adapter *padapter, uint ps_mode,
 			uint smart_ps);
 void r8712_set_rpwm(struct _adapter *padapter, u8 val8);
+void r8712_flush_rwctrl_works(struct _adapter *padapter);
 
 #endif  /* __RTL871X_PWRCTRL_H_ */
drivers/staging/rtl8712/usb_intf.c +2 -1
index 2434b13c8b12..643f21eb1128 100644
@@ -606,7 +606,8 @@ static void r871xu_dev_remove(struct usb_interface *pusb_intf)
 			padapter->surprise_removed = true;
 		if (pnetdev->reg_state != NETREG_UNINITIALIZED)
 			unregister_netdev(pnetdev); /* will call netdev_close() */
-		flush_scheduled_work();
+		r8712_flush_rwctrl_works(padapter);
+		r8712_flush_led_works(padapter);
 		udelay(1);
 		/* Stop driver mlme relation timer */
 		r8712_stop_drv_timers(padapter);
Signature · 1 line
2.32.0
#2 lore raw

PATCH 2/2 staging: rtl8712: error handling refactoring

In reply to Pavel Skripkin

There was strange error handling logic in case of fw load failure. For some reason fw loader callback was doing clean up stuff when fw is not available. I don't see any reason behind doing this. Since this driver doesn't have EEPROM firmware let's just disconnect it in case of fw load failure. Doing clean up stuff in 2 different place which can run concurently is not good idea and syzbot found 2 bugs related to this strange approach.

So, in this pacth I deleted all clean up code from fw callback and made a call to device_release_driver() under device_lock(parent) in case of fw load failure. This approach is more generic and it defend driver from UAF bugs, since all clean up code is moved to one place.

Fixes e02a3b945816 ("staging: rtl8712: fix memory leak in rtl871x_load_fw_cb")
Fixes 8c213fa59199 ("staging: r8712u: Use asynchronous firmware loading")
Reported-and-tested-by [email protected]
Reported-and-tested-by [email protected]
---
 drivers/staging/rtl8712/hal_init.c | 30 +++++++++++------
 drivers/staging/rtl8712/usb_intf.c | 52 +++++++++++++-----------------
 2 files changed, 43 insertions(+), 39 deletions(-)
drivers/staging/rtl8712/hal_init.c +20 -10
index 22974277afa0..4eff3fdecdb8 100644
@@ -29,21 +29,31 @@
 #define FWBUFF_ALIGN_SZ 512
 #define MAX_DUMP_FWSZ (48 * 1024)
 
+static void rtl871x_load_fw_fail(struct _adapter *adapter)
+{
+	struct usb_device *udev = adapter->dvobjpriv.pusbdev;
+	struct device *dev = &udev->dev;
+	struct device *parent = dev->parent;
+
+	complete(&adapter->rtl8712_fw_ready);
+
+	dev_err(&udev->dev, "r8712u: Firmware request failed\n");
+
+	if (parent)
+		device_lock(parent);
+
+	device_release_driver(dev);
+
+	if (parent)
+		device_unlock(parent);
+}
+
 static void rtl871x_load_fw_cb(const struct firmware *firmware, void *context)
 {
 	struct _adapter *adapter = context;
 
 	if (!firmware) {
-		struct usb_device *udev = adapter->dvobjpriv.pusbdev;
-		struct usb_interface *usb_intf = adapter->pusb_intf;
-
-		dev_err(&udev->dev, "r8712u: Firmware request failed\n");
-		usb_put_dev(udev);
-		usb_set_intfdata(usb_intf, NULL);
-		r8712_free_drv_sw(adapter);
-		adapter->dvobj_deinit(adapter);
-		complete(&adapter->rtl8712_fw_ready);
-		free_netdev(adapter->pnetdev);
+		rtl871x_load_fw_fail(adapter);
 		return;
 	}
 	adapter->fw = firmware;
drivers/staging/rtl8712/usb_intf.c +23 -29

59 lines not on this page · raw message

Signature · 1 line
2.32.0