]> git.hungrycats.org Git - linux/commitdiff
USB: cdc-wdm: fix buffer overflow
authorOliver Neukum <oneukum@suse.de>
Tue, 12 Mar 2013 13:52:42 +0000 (14:52 +0100)
committerWilly Tarreau <w@1wt.eu>
Mon, 10 Jun 2013 09:43:05 +0000 (11:43 +0200)
commit c0f5ecee4e741667b2493c742b60b6218d40b3aa upstream.

The buffer for responses must not overflow.
If this would happen, set a flag, drop the data and return
an error after user space has read all remaining data.

Signed-off-by: Oliver Neukum <oliver@neukum.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
[bwh: Backported to 2.6.32: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
Signed-off-by: Willy Tarreau <w@1wt.eu>
drivers/usb/class/cdc-wdm.c

index 37f2899ee3b5c706cfb8f3111f518280feffe18e..01ae5191cb9463dafcdf2b927260a1282e2433db 100644 (file)
@@ -52,6 +52,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
 #define WDM_READ               4
 #define WDM_INT_STALL          5
 #define WDM_POLL_RUNNING       6
+#define WDM_OVERFLOW           10
 
 
 #define WDM_MAX                        16
@@ -115,6 +116,7 @@ static void wdm_in_callback(struct urb *urb)
 {
        struct wdm_device *desc = urb->context;
        int status = urb->status;
+       int length = urb->actual_length;
 
        spin_lock(&desc->iuspin);
 
@@ -144,9 +146,17 @@ static void wdm_in_callback(struct urb *urb)
        }
 
        desc->rerr = status;
-       desc->reslength = urb->actual_length;
-       memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength);
-       desc->length += desc->reslength;
+       if (length + desc->length > desc->wMaxCommand) {
+               /* The buffer would overflow */
+               set_bit(WDM_OVERFLOW, &desc->flags);
+       } else {
+               /* we may already be in overflow */
+               if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
+                       memmove(desc->ubuf + desc->length, desc->inbuf, length);
+                       desc->length += length;
+                       desc->reslength = length;
+               }
+       }
        wake_up(&desc->wait);
 
        set_bit(WDM_READ, &desc->flags);
@@ -398,6 +408,11 @@ retry:
                        rv = -ENODEV;
                        goto err;
                }
+               if (test_bit(WDM_OVERFLOW, &desc->flags)) {
+                       clear_bit(WDM_OVERFLOW, &desc->flags);
+                       rv = -ENOBUFS;
+                       goto err;
+               }
                i++;
                if (file->f_flags & O_NONBLOCK) {
                        if (!test_bit(WDM_READ, &desc->flags)) {
@@ -440,6 +455,7 @@ retry:
                        spin_unlock_irq(&desc->iuspin);
                        goto retry;
                }
+
                if (!desc->reslength) { /* zero length read */
                        dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
                        clear_bit(WDM_READ, &desc->flags);
@@ -844,6 +860,7 @@ static int wdm_post_reset(struct usb_interface *intf)
        struct wdm_device *desc = usb_get_intfdata(intf);
        int rv;
 
+       clear_bit(WDM_OVERFLOW, &desc->flags);
        rv = recover_from_urb_loss(desc);
        mutex_unlock(&desc->plock);
        return 0;