cdc-acm: prevent infinite loop when parsing CDC headers.
[pandora-kernel.git] / drivers / usb / class / cdc-acm.c
index ed988ef..57d6302 100644 (file)
@@ -191,7 +191,13 @@ static int acm_write_start(struct acm *acm, int wbn)
 
        dev_vdbg(&acm->data->dev, "%s - susp_count %d\n", __func__,
                                                        acm->susp_count);
-       usb_autopm_get_interface_async(acm->control);
+       rc = usb_autopm_get_interface_async(acm->control);
+       if (rc) {
+               wb->use = 0;
+               spin_unlock_irqrestore(&acm->write_lock, flags);
+               return rc;
+       }
+
        if (acm->susp_count) {
                usb_anchor_urb(wb->urb, &acm->delayed);
                spin_unlock_irqrestore(&acm->write_lock, flags);
@@ -813,11 +819,12 @@ static void acm_tty_set_termios(struct tty_struct *tty,
        /* FIXME: Needs to clear unsupported bits in the termios */
        acm->clocal = ((termios->c_cflag & CLOCAL) != 0);
 
-       if (!newline.dwDTERate) {
+       if (C_BAUD(tty) == B0) {
                newline.dwDTERate = acm->line.dwDTERate;
                newctrl &= ~ACM_CTRL_DTR;
-       } else
+       } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) {
                newctrl |=  ACM_CTRL_DTR;
+       }
 
        if (newctrl != acm->ctrlout)
                acm_set_control(acm, acm->ctrlout = newctrl);
@@ -904,6 +911,7 @@ static int acm_probe(struct usb_interface *intf,
        unsigned long quirks;
        int num_rx_buf;
        int i;
+       unsigned int elength = 0;
        int combined_interfaces = 0;
 
        /* normal quirks */
@@ -939,6 +947,12 @@ static int acm_probe(struct usb_interface *intf,
        }
 
        while (buflen > 0) {
+               elength = buffer[0];
+               if (!elength) {
+                       dev_err(&intf->dev, "skipping garbage byte\n");
+                       elength = 1;
+                       goto next_desc;
+               }
                if (buffer[1] != USB_DT_CS_INTERFACE) {
                        dev_err(&intf->dev, "skipping garbage\n");
                        goto next_desc;
@@ -946,6 +960,8 @@ static int acm_probe(struct usb_interface *intf,
 
                switch (buffer[2]) {
                case USB_CDC_UNION_TYPE: /* we've found it */
+                       if (elength < sizeof(struct usb_cdc_union_desc))
+                               goto next_desc;
                        if (union_header) {
                                dev_err(&intf->dev, "More than one "
                                        "union descriptor, skipping ...\n");
@@ -954,31 +970,38 @@ static int acm_probe(struct usb_interface *intf,
                        union_header = (struct usb_cdc_union_desc *)buffer;
                        break;
                case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+                       if (elength < sizeof(struct usb_cdc_country_functional_desc))
+                               goto next_desc;
                        cfd = (struct usb_cdc_country_functional_desc *)buffer;
                        break;
                case USB_CDC_HEADER_TYPE: /* maybe check version */
                        break; /* for now we ignore it */
                case USB_CDC_ACM_TYPE:
+                       if (elength < 4)
+                               goto next_desc;
                        ac_management_function = buffer[3];
                        break;
                case USB_CDC_CALL_MANAGEMENT_TYPE:
+                       if (elength < 5)
+                               goto next_desc;
                        call_management_function = buffer[3];
                        call_interface_num = buffer[4];
                        if ( (quirks & NOT_A_MODEM) == 0 && (call_management_function & 3) != 3)
                                dev_err(&intf->dev, "This device cannot do calls on its own. It is not a modem.\n");
                        break;
                default:
-                       /* there are LOTS more CDC descriptors that
+                       /*
+                        * there are LOTS more CDC descriptors that
                         * could legitimately be found here.
                         */
                        dev_dbg(&intf->dev, "Ignoring descriptor: "
-                                       "type %02x, length %d\n",
-                                       buffer[2], buffer[0]);
+                                       "type %02x, length %ud\n",
+                                       buffer[2], elength);
                        break;
                }
 next_desc:
-               buflen -= buffer[0];
-               buffer += buffer[0];
+               buflen -= elength;
+               buffer += elength;
        }
 
        if (!union_header) {
@@ -1004,10 +1027,11 @@ next_desc:
        } else {
                control_interface = usb_ifnum_to_if(usb_dev, union_header->bMasterInterface0);
                data_interface = usb_ifnum_to_if(usb_dev, (data_interface_num = union_header->bSlaveInterface0));
-               if (!control_interface || !data_interface) {
-                       dev_dbg(&intf->dev, "no interfaces\n");
-                       return -ENODEV;
-               }
+       }
+
+       if (!control_interface || !data_interface) {
+               dev_dbg(&intf->dev, "no interfaces\n");
+               return -ENODEV;
        }
 
        if (data_interface_num != call_interface_num)
@@ -1503,6 +1527,7 @@ static const struct usb_device_id acm_ids[] = {
        { USB_DEVICE(0x0572, 0x1328), /* Shiro / Aztech USB MODEM UM-3100 */
        .driver_info = NO_UNION_NORMAL, /* has no union descriptor */
        },
+       { USB_DEVICE(0x2184, 0x001c) }, /* GW Instek AFG-2225 */
        { USB_DEVICE(0x22b8, 0x6425), /* Motorola MOTOMAGX phones */
        },
        /* Motorola H24 HSPA module: */