Skip to content

Commit 1426bd2

Browse files
oneukumgregkh
authored andcommitted
USB: cdc-wdm: fix race between write and disconnect due to flag abuse
In case of a disconnect an ongoing flush() has to be made fail. Nevertheless we cannot be sure that any pending URB has already finished, so although they will never succeed, they still must not be touched. The clean solution for this is to check for WDM_IN_USE and WDM_DISCONNECTED in flush(). There is no point in ever clearing WDM_IN_USE, as no further writes make sense. The issue is as old as the driver. Fixes: afba937 ("USB: CDC WDM driver") Reported-by: [email protected] Signed-off-by: Oliver Neukum <[email protected]> Cc: stable <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Greg Kroah-Hartman <[email protected]>
1 parent 636bd02 commit 1426bd2

File tree

1 file changed

+12
-4
lines changed

1 file changed

+12
-4
lines changed

drivers/usb/class/cdc-wdm.c

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -587,10 +587,20 @@ static int wdm_flush(struct file *file, fl_owner_t id)
587587
{
588588
struct wdm_device *desc = file->private_data;
589589

590-
wait_event(desc->wait, !test_bit(WDM_IN_USE, &desc->flags));
590+
wait_event(desc->wait,
591+
/*
592+
* needs both flags. We cannot do with one
593+
* because resetting it would cause a race
594+
* with write() yet we need to signal
595+
* a disconnect
596+
*/
597+
!test_bit(WDM_IN_USE, &desc->flags) ||
598+
test_bit(WDM_DISCONNECTING, &desc->flags));
591599

592600
/* cannot dereference desc->intf if WDM_DISCONNECTING */
593-
if (desc->werr < 0 && !test_bit(WDM_DISCONNECTING, &desc->flags))
601+
if (test_bit(WDM_DISCONNECTING, &desc->flags))
602+
return -ENODEV;
603+
if (desc->werr < 0)
594604
dev_err(&desc->intf->dev, "Error in flush path: %d\n",
595605
desc->werr);
596606

@@ -974,8 +984,6 @@ static void wdm_disconnect(struct usb_interface *intf)
974984
spin_lock_irqsave(&desc->iuspin, flags);
975985
set_bit(WDM_DISCONNECTING, &desc->flags);
976986
set_bit(WDM_READ, &desc->flags);
977-
/* to terminate pending flushes */
978-
clear_bit(WDM_IN_USE, &desc->flags);
979987
spin_unlock_irqrestore(&desc->iuspin, flags);
980988
wake_up_all(&desc->wait);
981989
mutex_lock(&desc->rlock);

0 commit comments

Comments
 (0)