patch "USB: cdc-wdm: Fix use after free in service_outstanding_interrupt()." added to usb-linus
Subsystems (2)
This is a note to let you know that I've just added the patch titled
USB: cdc-wdm: Fix use after free in service_outstanding_interrupt().
to my usb git tree which can be found at
git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git
in the usb-linus branch.
The patch will show up in the next release of the linux-next tree (usually sometime within the next 24 hours during the week.)
The patch will hopefully also be merged in Linus's tree for the next -rc kernel release.
If you have any questions about this process, please let me know.
From 5e5ff0b4b6bcb4d17b7a26ec8bcfc7dd4651684f Mon Sep 17 00:00:00 2001
From: Tetsuo Handa <[email protected]>
Date: Sun, 20 Dec 2020 00:25:53 +0900
Subject: USB: cdc-wdm: Fix use after free in service_outstanding_interrupt().
syzbot is reporting UAF at usb_submit_urb() [1], for service_outstanding_interrupt() is not checking WDM_DISCONNECTING before calling usb_submit_urb(). Close the race by doing same checks wdm_read() does upon retry.
Also, while wdm_read() checks WDM_DISCONNECTING with desc->rlock held, service_interrupt_work() does not hold desc->rlock. Thus, it is possible that usb_submit_urb() is called from service_outstanding_interrupt() from service_interrupt_work() after WDM_DISCONNECTING was set and kill_urbs()
from wdm_disconnect() completed. Thus, move kill_urbs() inwdm_disconnect() to after cancel_work_sync() (which makes sure that service_interrupt_work() is no longer running) completed.
Although it seems to be safe to dereference desc->intf->dev in service_outstanding_interrupt() even if WDM_DISCONNECTING was already set because desc->rlock or cancel_work_sync() prevents wdm_disconnect() from reaching list_del() before service_outstanding_interrupt() completes, let's not emit error message if WDM_DISCONNECTING is set by wdm_disconnect() while usb_submit_urb() is in progress.
[1] https://syzkaller.appspot.com/bug?extid=9e04e2df4a32fb661daf
--- drivers/usb/class/cdc-wdm.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
drivers/usb/class/cdc-wdm.c +13 -3
@@ -465,13 +465,23 @@ static int service_outstanding_interrupt(struct wdm_device *desc)if (!desc->resp_count || !--desc->resp_count) goto out; +if (test_bit (WDM_DISCONNECTING , &desc->flags)) { + rv = -ENODEV ; + goto out; + } +if (test_bit (WDM_RESETTING , &desc->flags)) { + rv = -EIO ; + goto out; + } +set_bit (WDM_RESPONDING , &desc->flags);spin_unlock_irq (&desc->iuspin); rv =usb_submit_urb (desc->response,GFP_KERNEL );spin_lock_irq (&desc->iuspin);if (rv) { -dev_err (&desc->intf->dev, -"usb_submit_urb failed with result %d\n" , rv); +if (!test_bit (WDM_DISCONNECTING , &desc->flags)) +dev_err (&desc->intf->dev, +"usb_submit_urb failed with result %d\n" , rv);/* make sure the next notification trigger a submit */ clear_bit (WDM_RESPONDING , &desc->flags);
@@ -1027,9 +1037,9 @@ static void wdm_disconnect(struct usb_interface *intf)wake_up_all (&desc->wait);mutex_lock (&desc->rlock);mutex_lock (&desc->wlock); -kill_urbs (desc);cancel_work_sync (&desc->rxwork);cancel_work_sync (&desc->service_outs_intr); +kill_urbs (desc);mutex_unlock (&desc->wlock);mutex_unlock (&desc->rlock);
Signature · 1 line
2.29.2