mirror of https://gitee.com/openkylin/linux.git
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() in wdm_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 Reported-by: syzbot <syzbot+9e04e2df4a32fb661daf@syzkaller.appspotmail.com> Signed-off-by: Tetsuo Handa <penguin-kernel@I-love.SAKURA.ne.jp> Cc: stable <stable@vger.kernel.org> Link: https://lore.kernel.org/r/620e2ee0-b9a3-dbda-a25b-a93e0ed03ec5@i-love.sakura.ne.jp Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
a390bef7db
commit
5e5ff0b4b6
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue