aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class/cdc-wdm.c
diff options
context:
space:
mode:
authorOliver Neukum <oneukum@suse.de>2013-03-12 09:52:42 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-03-12 19:33:05 -0400
commitc0f5ecee4e741667b2493c742b60b6218d40b3aa (patch)
tree64f1a695a53a28eaaebe90a0816746d28ab63511 /drivers/usb/class/cdc-wdm.c
parenta57e82a18779ab8a5e5a1f5841cef937cf578913 (diff)
USB: cdc-wdm: fix buffer overflow
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> CC: stable@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/class/cdc-wdm.c')
-rw-r--r--drivers/usb/class/cdc-wdm.c23
1 files changed, 20 insertions, 3 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 5f0cb417b736..122d056d96d5 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -56,6 +56,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
56#define WDM_RESPONDING 7 56#define WDM_RESPONDING 7
57#define WDM_SUSPENDING 8 57#define WDM_SUSPENDING 8
58#define WDM_RESETTING 9 58#define WDM_RESETTING 9
59#define WDM_OVERFLOW 10
59 60
60#define WDM_MAX 16 61#define WDM_MAX 16
61 62
@@ -155,6 +156,7 @@ static void wdm_in_callback(struct urb *urb)
155{ 156{
156 struct wdm_device *desc = urb->context; 157 struct wdm_device *desc = urb->context;
157 int status = urb->status; 158 int status = urb->status;
159 int length = urb->actual_length;
158 160
159 spin_lock(&desc->iuspin); 161 spin_lock(&desc->iuspin);
160 clear_bit(WDM_RESPONDING, &desc->flags); 162 clear_bit(WDM_RESPONDING, &desc->flags);
@@ -185,9 +187,17 @@ static void wdm_in_callback(struct urb *urb)
185 } 187 }
186 188
187 desc->rerr = status; 189 desc->rerr = status;
188 desc->reslength = urb->actual_length; 190 if (length + desc->length > desc->wMaxCommand) {
189 memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); 191 /* The buffer would overflow */
190 desc->length += desc->reslength; 192 set_bit(WDM_OVERFLOW, &desc->flags);
193 } else {
194 /* we may already be in overflow */
195 if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
196 memmove(desc->ubuf + desc->length, desc->inbuf, length);
197 desc->length += length;
198 desc->reslength = length;
199 }
200 }
191skip_error: 201skip_error:
192 wake_up(&desc->wait); 202 wake_up(&desc->wait);
193 203
@@ -435,6 +445,11 @@ retry:
435 rv = -ENODEV; 445 rv = -ENODEV;
436 goto err; 446 goto err;
437 } 447 }
448 if (test_bit(WDM_OVERFLOW, &desc->flags)) {
449 clear_bit(WDM_OVERFLOW, &desc->flags);
450 rv = -ENOBUFS;
451 goto err;
452 }
438 i++; 453 i++;
439 if (file->f_flags & O_NONBLOCK) { 454 if (file->f_flags & O_NONBLOCK) {
440 if (!test_bit(WDM_READ, &desc->flags)) { 455 if (!test_bit(WDM_READ, &desc->flags)) {
@@ -478,6 +493,7 @@ retry:
478 spin_unlock_irq(&desc->iuspin); 493 spin_unlock_irq(&desc->iuspin);
479 goto retry; 494 goto retry;
480 } 495 }
496
481 if (!desc->reslength) { /* zero length read */ 497 if (!desc->reslength) { /* zero length read */
482 dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); 498 dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
483 clear_bit(WDM_READ, &desc->flags); 499 clear_bit(WDM_READ, &desc->flags);
@@ -1004,6 +1020,7 @@ static int wdm_post_reset(struct usb_interface *intf)
1004 struct wdm_device *desc = wdm_find_device(intf); 1020 struct wdm_device *desc = wdm_find_device(intf);
1005 int rv; 1021 int rv;
1006 1022
1023 clear_bit(WDM_OVERFLOW, &desc->flags);
1007 clear_bit(WDM_RESETTING, &desc->flags); 1024 clear_bit(WDM_RESETTING, &desc->flags);
1008 rv = recover_from_urb_loss(desc); 1025 rv = recover_from_urb_loss(desc);
1009 mutex_unlock(&desc->wlock); 1026 mutex_unlock(&desc->wlock);