Commit 9472aff1 authored by Alan Stern's avatar Alan Stern Committed by Greg Kroah-Hartman

USB: rio500: Fix lockdep violation

The syzbot fuzzer found a lockdep violation in the rio500 driver:

	======================================================
	WARNING: possible circular locking dependency detected
	5.3.0-rc2+ #23 Not tainted
	------------------------------------------------------
	syz-executor.2/20386 is trying to acquire lock:
	00000000772249c6 (rio500_mutex){+.+.}, at: open_rio+0x16/0xc0
	drivers/usb/misc/rio500.c:64

	but task is already holding lock:
	00000000d3e8f4b9 (minor_rwsem){++++}, at: usb_open+0x23/0x270
	drivers/usb/core/file.c:39

	which lock already depends on the new lock.

The problem is that the driver's open_rio() routine is called while
the usbcore's minor_rwsem is locked for reading, and it acquires the
rio500_mutex; whereas conversely, probe_rio() and disconnect_rio()
first acquire the rio500_mutex and then call usb_register_dev() or
usb_deregister_dev(), which lock minor_rwsem for writing.

The correct ordering of acquisition should be: minor_rwsem first, then
rio500_mutex (since the locking in open_rio() cannot be changed).
Thus, the probe and disconnect routines should avoid holding
rio500_mutex while doing their registration and deregistration.

This patch adjusts the code in those two routines to do just that.  It
also relies on the fact that the probe and disconnect routines are
protected by the device mutex, so the initial test of rio->present
needs no extra locking.

Reported-by: syzbot+7bbcbe9c9ff0cd49592a@syzkaller.appspotmail.com
Signed-off-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Fixes: d710734b ("USB: rio500: simplify locking")
Acked-by: default avatarOliver Neukum <oneukum@suse.com>
CC: <stable@vger.kernel.org>

Link: https://lore.kernel.org/r/Pine.LNX.4.44L0.1908081329240.1319-100000@iolanthe.rowland.orgSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 95e29e9b
...@@ -454,51 +454,55 @@ static int probe_rio(struct usb_interface *intf, ...@@ -454,51 +454,55 @@ static int probe_rio(struct usb_interface *intf,
{ {
struct usb_device *dev = interface_to_usbdev(intf); struct usb_device *dev = interface_to_usbdev(intf);
struct rio_usb_data *rio = &rio_instance; struct rio_usb_data *rio = &rio_instance;
int retval = 0; int retval = -ENOMEM;
char *ibuf, *obuf;
mutex_lock(&rio500_mutex);
if (rio->present) { if (rio->present) {
dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum); dev_info(&intf->dev, "Second USB Rio at address %d refused\n", dev->devnum);
retval = -EBUSY; return -EBUSY;
goto bail_out;
} else {
dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
}
retval = usb_register_dev(intf, &usb_rio_class);
if (retval) {
dev_err(&dev->dev,
"Not able to get a minor for this device.\n");
retval = -ENOMEM;
goto bail_out;
} }
dev_info(&intf->dev, "USB Rio found at address %d\n", dev->devnum);
rio->rio_dev = dev; obuf = kmalloc(OBUF_SIZE, GFP_KERNEL);
if (!obuf) {
if (!(rio->obuf = kmalloc(OBUF_SIZE, GFP_KERNEL))) {
dev_err(&dev->dev, dev_err(&dev->dev,
"probe_rio: Not enough memory for the output buffer\n"); "probe_rio: Not enough memory for the output buffer\n");
usb_deregister_dev(intf, &usb_rio_class); goto err_obuf;
retval = -ENOMEM;
goto bail_out;
} }
dev_dbg(&intf->dev, "obuf address:%p\n", rio->obuf); dev_dbg(&intf->dev, "obuf address: %p\n", obuf);
if (!(rio->ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL))) { ibuf = kmalloc(IBUF_SIZE, GFP_KERNEL);
if (!ibuf) {
dev_err(&dev->dev, dev_err(&dev->dev,
"probe_rio: Not enough memory for the input buffer\n"); "probe_rio: Not enough memory for the input buffer\n");
usb_deregister_dev(intf, &usb_rio_class); goto err_ibuf;
kfree(rio->obuf);
retval = -ENOMEM;
goto bail_out;
} }
dev_dbg(&intf->dev, "ibuf address:%p\n", rio->ibuf); dev_dbg(&intf->dev, "ibuf address: %p\n", ibuf);
usb_set_intfdata (intf, rio); mutex_lock(&rio500_mutex);
rio->rio_dev = dev;
rio->ibuf = ibuf;
rio->obuf = obuf;
rio->present = 1; rio->present = 1;
bail_out:
mutex_unlock(&rio500_mutex); mutex_unlock(&rio500_mutex);
retval = usb_register_dev(intf, &usb_rio_class);
if (retval) {
dev_err(&dev->dev,
"Not able to get a minor for this device.\n");
goto err_register;
}
usb_set_intfdata(intf, rio);
return retval;
err_register:
mutex_lock(&rio500_mutex);
rio->present = 0;
mutex_unlock(&rio500_mutex);
err_ibuf:
kfree(obuf);
err_obuf:
return retval; return retval;
} }
...@@ -507,10 +511,10 @@ static void disconnect_rio(struct usb_interface *intf) ...@@ -507,10 +511,10 @@ static void disconnect_rio(struct usb_interface *intf)
struct rio_usb_data *rio = usb_get_intfdata (intf); struct rio_usb_data *rio = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL); usb_set_intfdata (intf, NULL);
mutex_lock(&rio500_mutex);
if (rio) { if (rio) {
usb_deregister_dev(intf, &usb_rio_class); usb_deregister_dev(intf, &usb_rio_class);
mutex_lock(&rio500_mutex);
if (rio->isopen) { if (rio->isopen) {
rio->isopen = 0; rio->isopen = 0;
/* better let it finish - the release will do whats needed */ /* better let it finish - the release will do whats needed */
...@@ -524,8 +528,8 @@ static void disconnect_rio(struct usb_interface *intf) ...@@ -524,8 +528,8 @@ static void disconnect_rio(struct usb_interface *intf)
dev_info(&intf->dev, "USB Rio disconnected.\n"); dev_info(&intf->dev, "USB Rio disconnected.\n");
rio->present = 0; rio->present = 0;
}
mutex_unlock(&rio500_mutex); mutex_unlock(&rio500_mutex);
}
} }
static const struct usb_device_id rio_table[] = { static const struct usb_device_id rio_table[] = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment