Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

authored by

Alan Stern and committed by
Greg Kroah-Hartman
eb764c4b feccc30d

+54 -19
+4 -4
Documentation/usb/persist.txt
··· 136 136 137 137 If you replace one USB device with another of the same type (same 138 138 manufacturer, same IDs, and so on) there's an excellent chance the 139 - kernel won't detect the change. Serial numbers and other strings are 140 - not compared. In many cases it wouldn't help if they were, because 141 - manufacturers frequently omit serial numbers entirely in their 142 - devices. 139 + kernel won't detect the change. The serial number string and other 140 + descriptors are compared with the kernel's stored values, but this 141 + might not help since manufacturers frequently omit serial numbers 142 + entirely in their devices. 143 143 144 144 Furthermore it's quite possible to leave a USB device exactly the same 145 145 while changing its media. If you replace the flash memory card in a
+49 -14
drivers/usb/core/hub.c
··· 3010 3010 usb_deregister(&hub_driver); 3011 3011 } /* usb_hub_cleanup() */ 3012 3012 3013 - static int config_descriptors_changed(struct usb_device *udev) 3013 + static int descriptors_changed(struct usb_device *udev, 3014 + struct usb_device_descriptor *old_device_descriptor) 3014 3015 { 3015 - unsigned index; 3016 - unsigned len = 0; 3017 - struct usb_config_descriptor *buf; 3016 + int changed = 0; 3017 + unsigned index; 3018 + unsigned serial_len = 0; 3019 + unsigned len; 3020 + unsigned old_length; 3021 + int length; 3022 + char *buf; 3018 3023 3024 + if (memcmp(&udev->descriptor, old_device_descriptor, 3025 + sizeof(*old_device_descriptor)) != 0) 3026 + return 1; 3027 + 3028 + /* Since the idVendor, idProduct, and bcdDevice values in the 3029 + * device descriptor haven't changed, we will assume the 3030 + * Manufacturer and Product strings haven't changed either. 3031 + * But the SerialNumber string could be different (e.g., a 3032 + * different flash card of the same brand). 3033 + */ 3034 + if (udev->serial) 3035 + serial_len = strlen(udev->serial) + 1; 3036 + 3037 + len = serial_len; 3019 3038 for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { 3020 - if (len < le16_to_cpu(udev->config[index].desc.wTotalLength)) 3021 - len = le16_to_cpu(udev->config[index].desc.wTotalLength); 3039 + old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); 3040 + len = max(len, old_length); 3022 3041 } 3042 + 3023 3043 buf = kmalloc(len, GFP_NOIO); 3024 3044 if (buf == NULL) { 3025 3045 dev_err(&udev->dev, "no mem to re-read configs after reset\n"); ··· 3047 3027 return 1; 3048 3028 } 3049 3029 for (index = 0; index < udev->descriptor.bNumConfigurations; index++) { 3050 - int length; 3051 - int old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); 3052 - 3030 + old_length = le16_to_cpu(udev->config[index].desc.wTotalLength); 3053 3031 length = usb_get_descriptor(udev, USB_DT_CONFIG, index, buf, 3054 3032 old_length); 3055 - if (length < old_length) { 3033 + if (length != old_length) { 3056 3034 dev_dbg(&udev->dev, "config index %d, error %d\n", 3057 3035 index, length); 3036 + changed = 1; 3058 3037 break; 3059 3038 } 3060 3039 if (memcmp (buf, udev->rawdescriptors[index], old_length) 3061 3040 != 0) { 3062 3041 dev_dbg(&udev->dev, "config index %d changed (#%d)\n", 3063 - index, buf->bConfigurationValue); 3042 + index, 3043 + ((struct usb_config_descriptor *) buf)-> 3044 + bConfigurationValue); 3045 + changed = 1; 3064 3046 break; 3065 3047 } 3066 3048 } 3049 + 3050 + if (!changed && serial_len) { 3051 + length = usb_string(udev, udev->descriptor.iSerialNumber, 3052 + buf, serial_len); 3053 + if (length + 1 != serial_len) { 3054 + dev_dbg(&udev->dev, "serial string error %d\n", 3055 + length); 3056 + changed = 1; 3057 + } else if (memcmp(buf, udev->serial, length) != 0) { 3058 + dev_dbg(&udev->dev, "serial string changed\n"); 3059 + changed = 1; 3060 + } 3061 + } 3062 + 3067 3063 kfree(buf); 3068 - return index != udev->descriptor.bNumConfigurations; 3064 + return changed; 3069 3065 } 3070 3066 3071 3067 /** ··· 3154 3118 goto re_enumerate; 3155 3119 3156 3120 /* Device might have changed firmware (DFU or similar) */ 3157 - if (memcmp(&udev->descriptor, &descriptor, sizeof descriptor) 3158 - || config_descriptors_changed (udev)) { 3121 + if (descriptors_changed(udev, &descriptor)) { 3159 3122 dev_info(&udev->dev, "device firmware changed\n"); 3160 3123 udev->descriptor = descriptor; /* for disconnect() calls */ 3161 3124 goto re_enumerate;
+1 -1
drivers/usb/core/message.c
··· 784 784 if (size <= 0 || !buf || !index) 785 785 return -EINVAL; 786 786 buf[0] = 0; 787 - tbuf = kmalloc(256, GFP_KERNEL); 787 + tbuf = kmalloc(256, GFP_NOIO); 788 788 if (!tbuf) 789 789 return -ENOMEM; 790 790