isight_firmware: Avoid crash on loading invalid firmware

Different tools generate slightly different formats of the isight
firmware. Ensure that the firmware buffer is not overrun, while still
ensuring that the correct amount of data is written if trailing data is
present.

Signed-off-by: Matthew Garrett <mjg@redhat.com>
Report-by: Justin Mattock <justinmattock@gmail.com>
Tested-by: Justin Mattock <justinmattock@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by Matthew Garrett and committed by Greg Kroah-Hartman 62b58848 6460a261

+16 -7
+16 -7
drivers/usb/misc/isight_firmware.c
··· 39 39 struct usb_device *dev = interface_to_usbdev(intf); 40 40 int llen, len, req, ret = 0; 41 41 const struct firmware *firmware; 42 - unsigned char *buf; 42 + unsigned char *buf = kmalloc(50, GFP_KERNEL); 43 43 unsigned char data[4]; 44 - char *ptr; 44 + u8 *ptr; 45 + 46 + if (!buf) 47 + return -ENOMEM; 45 48 46 49 if (request_firmware(&firmware, "isight.fw", &dev->dev) != 0) { 47 50 printk(KERN_ERR "Unable to load isight firmware\n"); ··· 62 59 goto out; 63 60 } 64 61 65 - while (1) { 62 + while (ptr+4 <= firmware->data+firmware->size) { 66 63 memcpy(data, ptr, 4); 67 64 len = (data[0] << 8 | data[1]); 68 65 req = (data[2] << 8 | data[3]); ··· 74 71 continue; 75 72 76 73 for (; len > 0; req += 50) { 77 - llen = len > 50 ? 50 : len; 74 + llen = min(len, 50); 78 75 len -= llen; 79 - 80 - buf = kmalloc(llen, GFP_KERNEL); 76 + if (ptr+llen > firmware->data+firmware->size) { 77 + printk(KERN_ERR 78 + "Malformed isight firmware"); 79 + ret = -ENODEV; 80 + goto out; 81 + } 81 82 memcpy(buf, ptr, llen); 82 83 83 84 ptr += llen; ··· 96 89 goto out; 97 90 } 98 91 99 - kfree(buf); 100 92 } 101 93 } 94 + 102 95 if (usb_control_msg 103 96 (dev, usb_sndctrlpipe(dev, 0), 0xa0, 0x40, 0xe600, 0, "\0", 1, 104 97 300) != 1) { 105 98 printk(KERN_ERR "isight firmware loading completion failed\n"); 106 99 ret = -ENODEV; 107 100 } 101 + 108 102 out: 103 + kfree(buf); 109 104 release_firmware(firmware); 110 105 return ret; 111 106 }