diff options
author | Russ Dill <russ.dill@gmail.com> | 2009-11-18 13:02:13 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-11 14:55:20 -0500 |
commit | a2582bd478c13c574d4c16ef1209d333f2a25935 (patch) | |
tree | 77fd2b7ab8b5f6696872f3cf7151ff3ae0c02554 | |
parent | 22a627ba81e0f75b994d37adb68761a9b9ef7186 (diff) |
USB: Close usb_find_interface race
USB drivers that create character devices call usb_register_dev in their
probe function. This associates the usb_interface device with that minor
number and creates the character device and announces it to the world.
However, the driver's probe function is called before the new
usb_interface is added to the driver's klist_devices.
This is a problem because userspace will respond to the character device
creation announcement by opening the character device. The driver's open
function will the call usb_find_interface to find the usb_interface
associated with that minor number. usb_find_interface will walk the
driver's list of devices and find the usb_interface with the matching
minor number.
Because the announcement happens before the usb_interface is added to the
driver's klist_devices, a race condition exists. A straightforward fix
is to walk the list of devices on usb_bus_type instead since the device
is added to that list before the announcement occurs.
bus_find_device calls get_device to bump the reference count on the found
device. It is arguable that the reference count should be dropped by the
caller of usb_find_interface instead of usb_find_interface, however,
the current users of usb_find_interface do not expect this.
Signed-off-by: Russ Dill <Russ.Dill@gmail.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r-- | drivers/usb/core/usb.c | 28 |
1 files changed, 10 insertions, 18 deletions
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index b1b85abb9a2d..d1e9440799de 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
@@ -130,24 +130,17 @@ struct usb_host_interface *usb_altnum_to_altsetting( | |||
130 | } | 130 | } |
131 | EXPORT_SYMBOL_GPL(usb_altnum_to_altsetting); | 131 | EXPORT_SYMBOL_GPL(usb_altnum_to_altsetting); |
132 | 132 | ||
133 | struct find_interface_arg { | ||
134 | int minor; | ||
135 | struct usb_interface *interface; | ||
136 | }; | ||
137 | |||
138 | static int __find_interface(struct device *dev, void *data) | 133 | static int __find_interface(struct device *dev, void *data) |
139 | { | 134 | { |
140 | struct find_interface_arg *arg = data; | 135 | int *minor = data; |
141 | struct usb_interface *intf; | 136 | struct usb_interface *intf; |
142 | 137 | ||
143 | if (!is_usb_interface(dev)) | 138 | if (!is_usb_interface(dev)) |
144 | return 0; | 139 | return 0; |
145 | 140 | ||
146 | intf = to_usb_interface(dev); | 141 | intf = to_usb_interface(dev); |
147 | if (intf->minor != -1 && intf->minor == arg->minor) { | 142 | if (intf->minor != -1 && intf->minor == *minor) |
148 | arg->interface = intf; | ||
149 | return 1; | 143 | return 1; |
150 | } | ||
151 | return 0; | 144 | return 0; |
152 | } | 145 | } |
153 | 146 | ||
@@ -156,21 +149,20 @@ static int __find_interface(struct device *dev, void *data) | |||
156 | * @drv: the driver whose current configuration is considered | 149 | * @drv: the driver whose current configuration is considered |
157 | * @minor: the minor number of the desired device | 150 | * @minor: the minor number of the desired device |
158 | * | 151 | * |
159 | * This walks the driver device list and returns a pointer to the interface | 152 | * This walks the bus device list and returns a pointer to the interface |
160 | * with the matching minor. Note, this only works for devices that share the | 153 | * with the matching minor. Note, this only works for devices that share the |
161 | * USB major number. | 154 | * USB major number. |
162 | */ | 155 | */ |
163 | struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) | 156 | struct usb_interface *usb_find_interface(struct usb_driver *drv, int minor) |
164 | { | 157 | { |
165 | struct find_interface_arg argb; | 158 | struct device *dev; |
166 | int retval; | 159 | |
160 | dev = bus_find_device(&usb_bus_type, NULL, &minor, __find_interface); | ||
161 | |||
162 | /* Drop reference count from bus_find_device */ | ||
163 | put_device(dev); | ||
167 | 164 | ||
168 | argb.minor = minor; | 165 | return dev ? to_usb_interface(dev) : NULL; |
169 | argb.interface = NULL; | ||
170 | /* eat the error, it will be in argb.interface */ | ||
171 | retval = driver_for_each_device(&drv->drvwrap.driver, NULL, &argb, | ||
172 | __find_interface); | ||
173 | return argb.interface; | ||
174 | } | 166 | } |
175 | EXPORT_SYMBOL_GPL(usb_find_interface); | 167 | EXPORT_SYMBOL_GPL(usb_find_interface); |
176 | 168 | ||