Commit eb764c4b authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: check serial-number string after device reset

This patch (as1048) extends the descriptor checking after a device is
reset.  Now the SerialNumber string descriptor is compared to its old
value, in addition to the device and configuration descriptors.

As a consequence, the kmalloc() call in usb_string() is now on the
error-handling pathway for usb-storage.  Hence its allocation type is
changed to GFO_NOIO.
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent feccc30d
...@@ -136,10 +136,10 @@ aren't guaranteed to be 100% accurate. ...@@ -136,10 +136,10 @@ aren't guaranteed to be 100% accurate.
If you replace one USB device with another of the same type (same If you replace one USB device with another of the same type (same
manufacturer, same IDs, and so on) there's an excellent chance the manufacturer, same IDs, and so on) there's an excellent chance the
kernel won't detect the change. Serial numbers and other strings are kernel won't detect the change. The serial number string and other
not compared. In many cases it wouldn't help if they were, because descriptors are compared with the kernel's stored values, but this
manufacturers frequently omit serial numbers entirely in their might not help since manufacturers frequently omit serial numbers
devices. entirely in their devices.
Furthermore it's quite possible to leave a USB device exactly the same Furthermore it's quite possible to leave a USB device exactly the same
while changing its media. If you replace the flash memory card in a while changing its media. If you replace the flash memory card in a
......
...@@ -3010,16 +3010,36 @@ void usb_hub_cleanup(void) ...@@ -3010,16 +3010,36 @@ void usb_hub_cleanup(void)
usb_deregister(&hub_driver); usb_deregister(&hub_driver);
} /* usb_hub_cleanup() */ } /* usb_hub_cleanup() */
static int config_descriptors_changed(struct usb_device *udev) static int descriptors_changed(struct usb_device *udev,
{ struct usb_device_descriptor *old_device_descriptor)
unsigned index; {
unsigned len = 0; int changed = 0;
struct usb_config_descriptor *buf; unsigned index;
unsigned serial_len = 0;
unsigned len;
unsigned old_length;
int length;
char *buf;
if (memcmp(&udev->descriptor, old_device_descriptor,
sizeof(*old_device_descriptor)) != 0)
return 1;
/* Since the idVendor, idProduct, and bcdDevice values in the
* device descriptor haven't changed, we will assume the
* Manufacturer and Product strings haven't changed either.
* But the SerialNumber string could be different (e.g., a
* different flash card of the same brand).
*/
if (udev->serial)
serial_len = strlen(udev->serial) + 1;
len = serial_len;
for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
if (len < le16_to_cpu(udev->config[index].desc.wTotalLength)) old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
len = le16_to_cpu(udev->config[index].desc.wTotalLength); len = max(len, old_length);
} }
buf = kmalloc(len, GFP_NOIO); buf = kmalloc(len, GFP_NOIO);
if (buf == NULL) { if (buf == NULL) {
dev_err(&udev->dev, "no mem to re-read configs after reset\n"); dev_err(&udev->dev, "no mem to re-read configs after reset\n");
...@@ -3027,25 +3047,41 @@ static int config_descriptors_changed(struct usb_device *udev) ...@@ -3027,25 +3047,41 @@ static int config_descriptors_changed(struct usb_device *udev)
return 1; return 1;
} }
for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { for (index = 0; index < udev->descriptor.bNumConfigurations; index++) {
int length; old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength);
length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf,
old_length); old_length);
if (length < old_length) { if (length != old_length) {
dev_dbg(&udev->dev, "config index %d, error %d\n", dev_dbg(&udev->dev, "config index %d, error %d\n",
index, length); index, length);
changed = 1;
break; break;
} }
if (memcmp (buf, udev->rawdescriptors[index], old_length) if (memcmp (buf, udev->rawdescriptors[index], old_length)
!= 0) { != 0) {
dev_dbg(&udev->dev, "config index %d changed (#%d)\n", dev_dbg(&udev->dev, "config index %d changed (#%d)\n",
index, buf->bConfigurationValue); index,
((struct usb_config_descriptor *) buf)->
bConfigurationValue);
changed = 1;
break; break;
} }
} }
if (!changed && serial_len) {
length = usb_string(udev, udev->descriptor.iSerialNumber,
buf, serial_len);
if (length + 1 != serial_len) {
dev_dbg(&udev->dev, "serial string error %d\n",
length);
changed = 1;
} else if (memcmp(buf, udev->serial, length) != 0) {
dev_dbg(&udev->dev, "serial string changed\n");
changed = 1;
}
}
kfree(buf); kfree(buf);
return index != udev->descriptor.bNumConfigurations; return changed;
} }
/** /**
...@@ -3118,8 +3154,7 @@ int usb_reset_device(struct usb_device *udev) ...@@ -3118,8 +3154,7 @@ int usb_reset_device(struct usb_device *udev)
goto re_enumerate; goto re_enumerate;
/* Device might have changed firmware (DFU or similar) */ /* Device might have changed firmware (DFU or similar) */
if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor) if (descriptors_changed(udev, &descriptor)) {
|| config_descriptors_changed (udev)) {
dev_info(&udev->dev, "device firmware changed\n"); dev_info(&udev->dev, "device firmware changed\n");
udev->descriptor = descriptor; /* for disconnect() calls */ udev->descriptor = descriptor; /* for disconnect() calls */
goto re_enumerate; goto re_enumerate;
......
...@@ -784,7 +784,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size) ...@@ -784,7 +784,7 @@ int usb_string(struct usb_device *dev, int index, char *buf, size_t size)
if (size <= 0 || !buf || !index) if (size <= 0 || !buf || !index)
return -EINVAL; return -EINVAL;
buf[0] = 0; buf[0] = 0;
tbuf = kmalloc(256, GFP_KERNEL); tbuf = kmalloc(256, GFP_NOIO);
if (!tbuf) if (!tbuf)
return -ENOMEM; return -ENOMEM;
......
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