usb: cdc-acm: Check control transfer buffer size before access
If the first fragment is shorter than struct usb_cdc_notification, we can't calculate an expected_size. Log an error and discard the notification instead of reading lengths from memory outside the received data, which can lead to memory corruption when the expected_size decreases between fragments, causing `expected_size - acm->nb_index` to wrap. This issue has been present since the beginning of git history; however, it only leads to memory corruption since commitea2583529c
("cdc-acm: reassemble fragmented notifications"). A mitigating factor is that acm_ctrl_irq() can only execute after userspace has opened /dev/ttyACM*; but if ModemManager is running, ModemManager will do that automatically depending on the USB device's vendor/product IDs and its other interfaces. Cc: stable <stable@kernel.org> Fixes:1da177e4c3
("Linux-2.6.12-rc2") Signed-off-by: Jann Horn <jannh@google.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
parent
c81d9fcd5b
commit
e563b01208
1 changed files with 14 additions and 3 deletions
|
@ -371,7 +371,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
|
|||
static void acm_ctrl_irq(struct urb *urb)
|
||||
{
|
||||
struct acm *acm = urb->context;
|
||||
struct usb_cdc_notification *dr = urb->transfer_buffer;
|
||||
struct usb_cdc_notification *dr;
|
||||
unsigned int current_size = urb->actual_length;
|
||||
unsigned int expected_size, copy_size, alloc_size;
|
||||
int retval;
|
||||
|
@ -398,9 +398,20 @@ static void acm_ctrl_irq(struct urb *urb)
|
|||
|
||||
usb_mark_last_busy(acm->dev);
|
||||
|
||||
if (acm->nb_index)
|
||||
if (acm->nb_index == 0) {
|
||||
/*
|
||||
* The first chunk of a message must contain at least the
|
||||
* notification header with the length field, otherwise we
|
||||
* can't get an expected_size.
|
||||
*/
|
||||
if (current_size < sizeof(struct usb_cdc_notification)) {
|
||||
dev_dbg(&acm->control->dev, "urb too short\n");
|
||||
goto exit;
|
||||
}
|
||||
dr = urb->transfer_buffer;
|
||||
} else {
|
||||
dr = (struct usb_cdc_notification *)acm->notification_buffer;
|
||||
|
||||
}
|
||||
/* size = notification-header + (optional) data */
|
||||
expected_size = sizeof(struct usb_cdc_notification) +
|
||||
le16_to_cpu(dr->wLength);
|
||||
|
|
Loading…
Add table
Reference in a new issue