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>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
a57e82a187
commit
c0f5ecee4e
@@ -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)
|
|||||||
{
|
{
|
||||||
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)
|
|||||||
}
|
}
|
||||||
|
|
||||||
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 @@ retry:
|
|||||||
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 @@ retry:
|
|||||||
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)
|
|||||||
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);
|
||||||
|
Reference in New Issue
Block a user