Patchwork [1/1] UBUNTU: SAUCE: khubd -- switch USB product/manufacturer/serial handling to RCU

login
register
mail settings
Submitter Andy Whitcroft
Date Feb. 17, 2010, 5:37 p.m.
Message ID <1266428225-31549-2-git-send-email-apw@canonical.com>
Download mbox | patch
Permalink /patch/45647/
State Accepted
Delegated to: Andy Whitcroft
Headers show

Comments

Andy Whitcroft - Feb. 17, 2010, 5:37 p.m.
BugLink: http://bugs.launchpad.net/bugs/510937

With the introduction of wireless USB hubs the product, manufacturer,
and serial number are now mutable.  This necessitates new locking in the
consumers of these values including the sysfs read routines in order to
prevent use-after-free acces to these values.  These extra locks create
significant lock contention leading to increased boot times (0.3s for an
example Atom based system).  Move update of these values to RCU based
locking.

Signed-off-by: Andy Whitcroft <apw@canonical.com>
---
 drivers/usb/core/hub.c   |   34 ++++++++++++++++++++++++++++------
 drivers/usb/core/sysfs.c |    6 +++---
 2 files changed, 31 insertions(+), 9 deletions(-)

Patch

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 1a7d54b..75804a9 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -22,6 +22,7 @@ 
 #include <linux/kthread.h>
 #include <linux/mutex.h>
 #include <linux/freezer.h>
+#include <linux/rcupdate.h>
 
 #include <asm/uaccess.h>
 #include <asm/byteorder.h>
@@ -1803,6 +1804,10 @@  fail:
  */
 int usb_deauthorize_device(struct usb_device *usb_dev)
 {
+	char *product = NULL;
+	char *manufacturer = NULL;
+	char *serial = NULL;
+
 	usb_lock_device(usb_dev);
 	if (usb_dev->authorized == 0)
 		goto out_unauthorized;
@@ -1810,11 +1815,12 @@  int usb_deauthorize_device(struct usb_device *usb_dev)
 	usb_dev->authorized = 0;
 	usb_set_configuration(usb_dev, -1);
 
-	kfree(usb_dev->product);
+	product = usb_dev->product;
+	manufacturer = usb_dev->manufacturer;
+	serial = usb_dev->serial;
+
 	usb_dev->product = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-	kfree(usb_dev->manufacturer);
 	usb_dev->manufacturer = kstrdup("n/a (unauthorized)", GFP_KERNEL);
-	kfree(usb_dev->serial);
 	usb_dev->serial = kstrdup("n/a (unauthorized)", GFP_KERNEL);
 
 	usb_destroy_configuration(usb_dev);
@@ -1822,6 +1828,12 @@  int usb_deauthorize_device(struct usb_device *usb_dev)
 
 out_unauthorized:
 	usb_unlock_device(usb_dev);
+	if (product || manufacturer || serial) {
+		synchronize_rcu();
+		kfree(product);
+		kfree(manufacturer);
+		kfree(serial);
+	}
 	return 0;
 }
 
@@ -1829,6 +1841,9 @@  out_unauthorized:
 int usb_authorize_device(struct usb_device *usb_dev)
 {
 	int result = 0, c;
+	char *product = NULL;
+	char *manufacturer = NULL;
+	char *serial = NULL;
 
 	usb_lock_device(usb_dev);
 	if (usb_dev->authorized == 1)
@@ -1847,11 +1862,12 @@  int usb_authorize_device(struct usb_device *usb_dev)
 		goto error_device_descriptor;
 	}
 
-	kfree(usb_dev->product);
+	
+	product = usb_dev->product;
 	usb_dev->product = NULL;
-	kfree(usb_dev->manufacturer);
+	manufacturer = usb_dev->manufacturer;
 	usb_dev->manufacturer = NULL;
-	kfree(usb_dev->serial);
+	serial = usb_dev->serial;
 	usb_dev->serial = NULL;
 
 	usb_dev->authorized = 1;
@@ -1879,6 +1895,12 @@  error_device_descriptor:
 error_autoresume:
 out_authorized:
 	usb_unlock_device(usb_dev);	// complements locktree
+	if (product || manufacturer || serial) {
+		synchronize_rcu();
+		kfree(product);
+		kfree(manufacturer);
+		kfree(serial);
+	}
 	return result;
 }
 
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index fcdcad4..25d2fa7 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -85,9 +85,9 @@  static ssize_t  show_##name(struct device *dev,				\
 	int retval;							\
 									\
 	udev = to_usb_device(dev);					\
-	usb_lock_device(udev);						\
-	retval = sprintf(buf, "%s\n", udev->name);			\
-	usb_unlock_device(udev);					\
+	rcu_read_lock();						\
+	retval = sprintf(buf, "%s\n", rcu_dereference(udev->name));	\
+	rcu_read_unlock();						\
 	return retval;							\
 }									\
 static DEVICE_ATTR(name, S_IRUGO, show_##name, NULL);