Commit c0f5ecee authored by Oliver Neukum's avatar Oliver Neukum Committed by Greg Kroah-Hartman

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: default avatarOliver Neukum <oliver@neukum.org>
CC: stable@kernel.org
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent a57e82a1
...@@ -56,6 +56,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids); ...@@ -56,6 +56,7 @@ MODULE_DEVICE_TABLE (usb, wdm_ids);
#define WDM_RESPONDING 7 #define WDM_RESPONDING 7
#define WDM_SUSPENDING 8 #define WDM_SUSPENDING 8
#define WDM_RESETTING 9 #define WDM_RESETTING 9
#define WDM_OVERFLOW 10
#define WDM_MAX 16 #define WDM_MAX 16
...@@ -155,6 +156,7 @@ static void wdm_in_callback(struct urb *urb) ...@@ -155,6 +156,7 @@ static void wdm_in_callback(struct urb *urb)
{ {
struct wdm_device *desc = urb->context; struct wdm_device *desc = urb->context;
int status = urb->status; int status = urb->status;
int length = urb->actual_length;
spin_lock(&desc->iuspin); spin_lock(&desc->iuspin);
clear_bit(WDM_RESPONDING, &desc->flags); clear_bit(WDM_RESPONDING, &desc->flags);
...@@ -185,9 +187,17 @@ static void wdm_in_callback(struct urb *urb) ...@@ -185,9 +187,17 @@ static void wdm_in_callback(struct urb *urb)
} }
desc->rerr = status; desc->rerr = status;
desc->reslength = urb->actual_length; if (length + desc->length > desc->wMaxCommand) {
memmove(desc->ubuf + desc->length, desc->inbuf, desc->reslength); /* The buffer would overflow */
desc->length += desc->reslength; 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: skip_error:
wake_up(&desc->wait); wake_up(&desc->wait);
...@@ -435,6 +445,11 @@ static ssize_t wdm_read ...@@ -435,6 +445,11 @@ static ssize_t wdm_read
rv = -ENODEV; rv = -ENODEV;
goto err; goto err;
} }
if (test_bit(WDM_OVERFLOW, &desc->flags)) {
clear_bit(WDM_OVERFLOW, &desc->flags);
rv = -ENOBUFS;
goto err;
}
i++; i++;
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
if (!test_bit(WDM_READ, &desc->flags)) { if (!test_bit(WDM_READ, &desc->flags)) {
...@@ -478,6 +493,7 @@ static ssize_t wdm_read ...@@ -478,6 +493,7 @@ static ssize_t wdm_read
spin_unlock_irq(&desc->iuspin); spin_unlock_irq(&desc->iuspin);
goto retry; goto retry;
} }
if (!desc->reslength) { /* zero length read */ if (!desc->reslength) { /* zero length read */
dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
clear_bit(WDM_READ, &desc->flags); clear_bit(WDM_READ, &desc->flags);
...@@ -1004,6 +1020,7 @@ static int wdm_post_reset(struct usb_interface *intf) ...@@ -1004,6 +1020,7 @@ static int wdm_post_reset(struct usb_interface *intf)
struct wdm_device *desc = wdm_find_device(intf); struct wdm_device *desc = wdm_find_device(intf);
int rv; int rv;
clear_bit(WDM_OVERFLOW, &desc->flags);
clear_bit(WDM_RESETTING, &desc->flags); clear_bit(WDM_RESETTING, &desc->flags);
rv = recover_from_urb_loss(desc); rv = recover_from_urb_loss(desc);
mutex_unlock(&desc->wlock); mutex_unlock(&desc->wlock);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment