lkml.org 
[lkml]   [2021]   [Jan]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 5.10 080/145] USB: cdc-wdm: Fix use after free in service_outstanding_interrupt().
    Date
    From: Tetsuo Handa <penguin-kernel@i-love.sakura.ne.jp>

    commit 5e5ff0b4b6bcb4d17b7a26ec8bcfc7dd4651684f upstream.

    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>

    ---
    drivers/usb/class/cdc-wdm.c | 16 +++++++++++++---
    1 file changed, 13 insertions(+), 3 deletions(-)

    --- a/drivers/usb/class/cdc-wdm.c
    +++ b/drivers/usb/class/cdc-wdm.c
    @@ -465,13 +465,23 @@ static int service_outstanding_interrupt
    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_in
    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);


    \
     
     \ /
      Last update: 2021-01-11 14:18    [W:4.047 / U:0.056 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site