[PATCH] USB: fix substandard locking for the sysfs files

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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@xxxxxxxxxxxxxxxxxxx>
CC: Hans de Goede <hdegoede@xxxxxxxxxx>

---

In theory, this could be applied to the stable kernels.  In practice, I 
doubt that it matters much.


[as1546]


 drivers/usb/core/sysfs.c |   53 +++++++++++++++++++++++------------------------
 1 file changed, 26 insertions(+), 27 deletions(-)

Index: usb-3.11/drivers/usb/core/sysfs.c
===================================================================
--- usb-3.11.orig/drivers/usb/core/sysfs.c
+++ usb-3.11/drivers/usb/core/sysfs.c
@@ -23,14 +23,16 @@ static ssize_t field##_show(struct devic
 {									\
 	struct usb_device *udev;					\
 	struct usb_host_config *actconfig;				\
+	ssize_t rc = 0;							\
 									\
 	udev = to_usb_device(dev);					\
+	usb_lock_device(udev);						\
 	actconfig = udev->actconfig;					\
 	if (actconfig)							\
-		return sprintf(buf, format_string,			\
+		rc = sprintf(buf, format_string,			\
 				actconfig->desc.field);			\
-	else								\
-		return 0;						\
+	usb_unlock_device(udev);					\
+	return rc;							\
 }									\
 
 #define usb_actconfig_attr(field, format_string)		\
@@ -45,12 +47,15 @@ static ssize_t bMaxPower_show(struct dev
 {
 	struct usb_device *udev;
 	struct usb_host_config *actconfig;
+	ssize_t rc = 0;
 
 	udev = to_usb_device(dev);
+	usb_lock_device(udev);
 	actconfig = udev->actconfig;
-	if (!actconfig)
-		return 0;
-	return sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+	if (actconfig)
+		rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig));
+	usb_unlock_device(udev);
+	return rc;
 }
 static DEVICE_ATTR_RO(bMaxPower);
 
@@ -59,12 +64,15 @@ static ssize_t configuration_show(struct
 {
 	struct usb_device *udev;
 	struct usb_host_config *actconfig;
+	ssize_t rc = 0;
 
 	udev = to_usb_device(dev);
+	usb_lock_device(udev);
 	actconfig = udev->actconfig;
-	if ((!actconfig) || (!actconfig->string))
-		return 0;
-	return sprintf(buf, "%s\n", actconfig->string);
+	if (actconfig && actconfig->string)
+		rc = sprintf(buf, "%s\n", actconfig->string);
+	usb_unlock_device(udev);
+	return rc;
 }
 static DEVICE_ATTR_RO(configuration);
 
@@ -764,6 +772,7 @@ read_descriptors(struct file *filp, stru
 	 * Following that are the raw descriptor entries for all the
 	 * configurations (config plus subsidiary descriptors).
 	 */
+	usb_lock_device(udev);
 	for (cfgno = -1; cfgno < udev->descriptor.bNumConfigurations &&
 			nleft > 0; ++cfgno) {
 		if (cfgno < 0) {
@@ -784,6 +793,7 @@ read_descriptors(struct file *filp, stru
 			off -= srclen;
 		}
 	}
+	usb_unlock_device(udev);
 	return count - nleft;
 }
 
@@ -870,9 +880,7 @@ static ssize_t interface_show(struct dev
 	char *string;
 
 	intf = to_usb_interface(dev);
-	string = intf->cur_altsetting->string;
-	barrier();		/* The altsetting might change! */
-
+	string = ACCESS_ONCE(intf->cur_altsetting->string);
 	if (!string)
 		return 0;
 	return sprintf(buf, "%s\n", string);
@@ -888,7 +896,7 @@ static ssize_t modalias_show(struct devi
 
 	intf = to_usb_interface(dev);
 	udev = interface_to_usbdev(intf);
-	alt = intf->cur_altsetting;
+	alt = ACCESS_ONCE(intf->cur_altsetting);
 
 	return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X"
 			"ic%02Xisc%02Xip%02Xin%02X\n",
@@ -909,23 +917,14 @@ static ssize_t supports_autosuspend_show
 					 struct device_attribute *attr,
 					 char *buf)
 {
-	struct usb_interface *intf;
-	struct usb_device *udev;
-	int ret;
+	int s;
 
-	intf = to_usb_interface(dev);
-	udev = interface_to_usbdev(intf);
-
-	usb_lock_device(udev);
+	device_lock(dev);
 	/* Devices will be autosuspended even when an interface isn't claimed */
-	if (!intf->dev.driver ||
-			to_usb_driver(intf->dev.driver)->supports_autosuspend)
-		ret = sprintf(buf, "%u\n", 1);
-	else
-		ret = sprintf(buf, "%u\n", 0);
-	usb_unlock_device(udev);
+	s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend);
+	device_unlock(dev);
 
-	return ret;
+	return sprintf(buf, "%u\n", s);
 }
 static DEVICE_ATTR_RO(supports_autosuspend);
 

--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html




[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux