[PATCH] USB: ub oops in block_uevent

In kernel 2.6.16, if a mounted storage device is removed, an oops happens
because ub supplies an interface device (and kobject) to the block layer,
but neglects to pin it. And apparently, the block layer expects its users
to pin device structures.

The code in ub was broken this way for years. But the bug was exposed only
by 2.6.16 when it started to call block_uevent on close, which traverses
device structures (kobjects actually).

Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
diff --git a/drivers/block/ub.c b/drivers/block/ub.c
index f73446f..c688c25 100644
--- a/drivers/block/ub.c
+++ b/drivers/block/ub.c
@@ -536,6 +536,9 @@
 		kfree(lun);
 	}
 
+	usb_set_intfdata(sc->intf, NULL);
+	usb_put_intf(sc->intf);
+	usb_put_dev(sc->dev);
 	kfree(sc);
 }
 
@@ -2221,7 +2224,12 @@
 	// sc->ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
 	usb_set_intfdata(intf, sc);
 	usb_get_dev(sc->dev);
-	// usb_get_intf(sc->intf);	/* Do we need this? */
+	/*
+	 * Since we give the interface struct to the block level through
+	 * disk->driverfs_dev, we have to pin it. Otherwise, block_uevent
+	 * oopses on close after a disconnect (kernels 2.6.16 and up).
+	 */
+	usb_get_intf(sc->intf);
 
 	snprintf(sc->name, 12, DRV_NAME "(%d.%d)",
 	    sc->dev->bus->busnum, sc->dev->devnum);
@@ -2286,7 +2294,7 @@
 
 err_dev_desc:
 	usb_set_intfdata(intf, NULL);
-	// usb_put_intf(sc->intf);
+	usb_put_intf(sc->intf);
 	usb_put_dev(sc->dev);
 	kfree(sc);
 err_core:
@@ -2461,12 +2469,6 @@
 	 * and no URBs left in transit.
 	 */
 
-	usb_set_intfdata(intf, NULL);
-	// usb_put_intf(sc->intf);
-	sc->intf = NULL;
-	usb_put_dev(sc->dev);
-	sc->dev = NULL;
-
 	ub_put(sc);
 }