diff options
author | Alan Stern <stern@rowland.harvard.edu> | 2013-09-24 15:43:39 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-09-25 20:27:01 -0400 |
commit | 232275a089dfd2e77377a85f11d0a4e3ca60e612 (patch) | |
tree | 14d96e68ff15d0751996e123ff6c9e7e8ff232b5 /drivers/usb/core | |
parent | 9a37a50349c8d6a7adcee21cefae407fb2f8c623 (diff) |
USB: fix substandard locking for the sysfs files
This patch straightens out some locking issues in the USB sysfs
interface:
Deauthorization will destroy existing configurations.
Attributes that read from udev->actconfig need to lock the
device to prevent races. Likewise for the rawdescriptor
values.
Attributes that access an interface's current alternate
setting should use ACCESS_ONCE() to obtain the cur_altsetting
pointer, to protect against concurrent altsetting changes.
The supports_autosuspend() attribute routine accesses values
from an interface's driver, so it should lock the interface
(rather than the usb_device) to protect against concurrent
unbinds. Once this is done, the routine can be simplified
considerably.
Scalar values that are stored directly in the usb_device structure are
always available. They do not require any locking. The same is true
of the cached interface string descriptor, because it is not
deallocated until the usb_host_interface structure is destroyed.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
CC: Hans de Goede <hdegoede@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/core')
-rw-r--r-- | drivers/usb/core/sysfs.c | 53 |
1 files changed, 26 insertions, 27 deletions
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index 6d2c8edb1ffe..59cb5f99467a 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c | |||
@@ -23,14 +23,16 @@ static ssize_t field##_show(struct device *dev, \ | |||
23 | { \ | 23 | { \ |
24 | struct usb_device *udev; \ | 24 | struct usb_device *udev; \ |
25 | struct usb_host_config *actconfig; \ | 25 | struct usb_host_config *actconfig; \ |
26 | ssize_t rc = 0; \ | ||
26 | \ | 27 | \ |
27 | udev = to_usb_device(dev); \ | 28 | udev = to_usb_device(dev); \ |
29 | usb_lock_device(udev); \ | ||
28 | actconfig = udev->actconfig; \ | 30 | actconfig = udev->actconfig; \ |
29 | if (actconfig) \ | 31 | if (actconfig) \ |
30 | return sprintf(buf, format_string, \ | 32 | rc = sprintf(buf, format_string, \ |
31 | actconfig->desc.field); \ | 33 | actconfig->desc.field); \ |
32 | else \ | 34 | usb_unlock_device(udev); \ |
33 | return 0; \ | 35 | return rc; \ |
34 | } \ | 36 | } \ |
35 | 37 | ||
36 | #define usb_actconfig_attr(field, format_string) \ | 38 | #define usb_actconfig_attr(field, format_string) \ |
@@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct device *dev, | |||
45 | { | 47 | { |
46 | struct usb_device *udev; | 48 | struct usb_device *udev; |
47 | struct usb_host_config *actconfig; | 49 | struct usb_host_config *actconfig; |
50 | ssize_t rc = 0; | ||
48 | 51 | ||
49 | udev = to_usb_device(dev); | 52 | udev = to_usb_device(dev); |
53 | usb_lock_device(udev); | ||
50 | actconfig = udev->actconfig; | 54 | actconfig = udev->actconfig; |
51 | if (!actconfig) | 55 | if (actconfig) |
52 | return 0; | 56 | rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); |
53 | return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); | 57 | usb_unlock_device(udev); |
58 | return rc; | ||
54 | } | 59 | } |
55 | static DEVICE_ATTR_RO(bMaxPower); | 60 | static DEVICE_ATTR_RO(bMaxPower); |
56 | 61 | ||
@@ -59,12 +64,15 @@ static ssize_t configuration_show(struct device *dev, | |||
59 | { | 64 | { |
60 | struct usb_device *udev; | 65 | struct usb_device *udev; |
61 | struct usb_host_config *actconfig; | 66 | struct usb_host_config *actconfig; |
67 | ssize_t rc = 0; | ||
62 | 68 | ||
63 | udev = to_usb_device(dev); | 69 | udev = to_usb_device(dev); |
70 | usb_lock_device(udev); | ||
64 | actconfig = udev->actconfig; | 71 | actconfig = udev->actconfig; |
65 | if ((!actconfig) || (!actconfig->string)) | 72 | if (actconfig && actconfig->string) |
66 | return 0; | 73 | rc = sprintf(buf, "%s\n", actconfig->string); |
67 | return sprintf(buf, "%s\n", actconfig->string); | 74 | usb_unlock_device(udev); |
75 | return rc; | ||
68 | } | 76 | } |
69 | static DEVICE_ATTR_RO(configuration); | 77 | static DEVICE_ATTR_RO(configuration); |
70 | 78 | ||
@@ -764,6 +772,7 @@ read_descriptors(struct file *filp, struct kobject *kobj, | |||
764 | * Following that are the raw descriptor entries for all the | 772 | * Following that are the raw descriptor entries for all the |
765 | * configurations (config plus subsidiary descriptors). | 773 | * configurations (config plus subsidiary descriptors). |
766 | */ | 774 | */ |
775 | usb_lock_device(udev); | ||
767 | for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations && | 776 | for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations && |
768 | nleft > 0; ++cfgno) { | 777 | nleft > 0; ++cfgno) { |
769 | if (cfgno < 0) { | 778 | if (cfgno < 0) { |
@@ -784,6 +793,7 @@ read_descriptors(struct file *filp, struct kobject *kobj, | |||
784 | off -= srclen; | 793 | off -= srclen; |
785 | } | 794 | } |
786 | } | 795 | } |
796 | usb_unlock_device(udev); | ||
787 | return count - nleft; | 797 | return count - nleft; |
788 | } | 798 | } |
789 | 799 | ||
@@ -870,9 +880,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr, | |||
870 | char *string; | 880 | char *string; |
871 | 881 | ||
872 | intf = to_usb_interface(dev); | 882 | intf = to_usb_interface(dev); |
873 | string = intf->cur_altsetting->string; | 883 | string = ACCESS_ONCE(intf->cur_altsetting->string); |
874 | barrier(); /* The altsetting might change! */ | ||
875 | |||
876 | if (!string) | 884 | if (!string) |
877 | return 0; | 885 | return 0; |
878 | return sprintf(buf, "%s\n", string); | 886 | return sprintf(buf, "%s\n", string); |
@@ -888,7 +896,7 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, | |||
888 | 896 | ||
889 | intf = to_usb_interface(dev); | 897 | intf = to_usb_interface(dev); |
890 | udev = interface_to_usbdev(intf); | 898 | udev = interface_to_usbdev(intf); |
891 | alt = intf->cur_altsetting; | 899 | alt = ACCESS_ONCE(intf->cur_altsetting); |
892 | 900 | ||
893 | return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" | 901 | return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" |
894 | "ic%02Xisc%02Xip%02Xin%02X\n", | 902 | "ic%02Xisc%02Xip%02Xin%02X\n", |
@@ -909,23 +917,14 @@ static ssize_t supports_autosuspend_show(struct device *dev, | |||
909 | struct device_attribute *attr, | 917 | struct device_attribute *attr, |
910 | char *buf) | 918 | char *buf) |
911 | { | 919 | { |
912 | struct usb_interface *intf; | 920 | int s; |
913 | struct usb_device *udev; | ||
914 | int ret; | ||
915 | 921 | ||
916 | intf = to_usb_interface(dev); | 922 | device_lock(dev); |
917 | udev = interface_to_usbdev(intf); | ||
918 | |||
919 | usb_lock_device(udev); | ||
920 | /* Devices will be autosuspended even when an interface isn't claimed */ | 923 | /* Devices will be autosuspended even when an interface isn't claimed */ |
921 | if (!intf->dev.driver || | 924 | s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend); |
922 | to_usb_driver(intf->dev.driver)->supports_autosuspend) | 925 | device_unlock(dev); |
923 | ret = sprintf(buf, "%u\n", 1); | ||
924 | else | ||
925 | ret = sprintf(buf, "%u\n", 0); | ||
926 | usb_unlock_device(udev); | ||
927 | 926 | ||
928 | return ret; | 927 | return sprintf(buf, "%u\n", s); |
929 | } | 928 | } |
930 | static DEVICE_ATTR_RO(supports_autosuspend); | 929 | static DEVICE_ATTR_RO(supports_autosuspend); |
931 | 930 | ||