88 lines
2.6 KiB
Diff
88 lines
2.6 KiB
Diff
From c0f5ecee4e741667b2493c742b60b6218d40b3aa Mon Sep 17 00:00:00 2001
|
|
From: Oliver Neukum <oneukum@suse.de>
|
|
Date: Tue, 12 Mar 2013 14:52:42 +0100
|
|
Subject: USB: cdc-wdm: fix buffer overflow
|
|
|
|
From: Oliver Neukum <oneukum@suse.de>
|
|
|
|
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>
|
|
|
|
---
|
|
drivers/usb/class/cdc-wdm.c | 23 ++++++++++++++++++++---
|
|
1 file changed, 20 insertions(+), 3 deletions(-)
|
|
|
|
--- a/drivers/usb/class/cdc-wdm.c
|
|
+++ b/drivers/usb/class/cdc-wdm.c
|
|
@@ -56,6 +56,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
|
|
#define WDM_RESPONDING 7
|
|
#define WDM_SUSPENDING 8
|
|
#define WDM_RESETTING 9
|
|
+#define WDM_OVERFLOW 10
|
|
|
|
#define WDM_MAX 16
|
|
|
|
@@ -155,6 +156,7 @@ static void wdm_in_callback(struct urb *
|
|
{
|
|
struct wdm_device *desc = urb->context;
|
|
int status = urb->status;
|
|
+ int length = urb->actual_length;
|
|
|
|
spin_lock(&desc->iuspin);
|
|
clear_bit(WDM_RESPONDING, &desc->flags);
|
|
@@ -185,9 +187,17 @@ static void wdm_in_callback(struct 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;
|
|
+ }
|
|
+ }
|
|
skip_error:
|
|
wake_up(&desc->wait);
|
|
|
|
@@ -435,6 +445,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)) {
|
|
@@ -478,6 +493,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);
|
|
@@ -1004,6 +1020,7 @@ static int wdm_post_reset(struct usb_int
|
|
struct wdm_device *desc = wdm_find_device(intf);
|
|
int rv;
|
|
|
|
+ clear_bit(WDM_OVERFLOW, &desc->flags);
|
|
clear_bit(WDM_RESETTING, &desc->flags);
|
|
rv = recover_from_urb_loss(desc);
|
|
mutex_unlock(&desc->wlock);
|