mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-07-22 03:27:30 -04:00
USB: idmouse: fix use-after-free on disconnect race
mutex_unlock() may access the mutex structure after releasing the lock and therefore cannot be used to manage lifetime of objects directly (unlike spinlocks and refcounts). [1][2] Use a kref to release the driver data to avoid use-after-free in mutex_unlock() when release() races with disconnect(). [1]a51749ab34("locking/mutex: Document that mutex_unlock() is non-atomic") [2]2b9d9e0a9b("locking/mutex: Clarify that mutex_unlock(), and most other sleeping locks, can still use the lock object after it's unlocked") Fixes:54d2bc068f("USB: fix locking in idmouse") Cc: stable@vger.kernel.org # 2.6.24 Cc: Oliver Neukum <oliver@neukum.org> Signed-off-by: Johan Hovold <johan@kernel.org> Link: https://patch.msgid.link/20260622152612.116422-3-johan@kernel.org Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
c602254ba4
commit
ff002c153f
@@ -63,6 +63,7 @@ MODULE_DEVICE_TABLE(usb, idmouse_table);
|
||||
|
||||
/* structure to hold all of our device specific stuff */
|
||||
struct usb_idmouse {
|
||||
struct kref kref;
|
||||
|
||||
struct usb_device *udev; /* save off the usb device pointer */
|
||||
struct usb_interface *interface; /* the interface for this device */
|
||||
@@ -209,8 +210,10 @@ static int idmouse_resume(struct usb_interface *intf)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void idmouse_delete(struct usb_idmouse *dev)
|
||||
static inline void idmouse_delete(struct kref *kref)
|
||||
{
|
||||
struct usb_idmouse *dev = container_of(kref, struct usb_idmouse, kref);
|
||||
|
||||
kfree(dev->bulk_in_buffer);
|
||||
kfree(dev);
|
||||
}
|
||||
@@ -254,6 +257,8 @@ static int idmouse_open(struct inode *inode, struct file *file)
|
||||
/* increment our usage count for the driver */
|
||||
++dev->open;
|
||||
|
||||
kref_get(&dev->kref);
|
||||
|
||||
/* save our object in the file's private structure */
|
||||
file->private_data = dev;
|
||||
|
||||
@@ -277,16 +282,11 @@ static int idmouse_release(struct inode *inode, struct file *file)
|
||||
|
||||
/* lock our device */
|
||||
mutex_lock(&dev->lock);
|
||||
|
||||
--dev->open;
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
kref_put(&dev->kref, idmouse_delete);
|
||||
|
||||
if (!dev->present) {
|
||||
/* the device was unplugged before the file was released */
|
||||
mutex_unlock(&dev->lock);
|
||||
idmouse_delete(dev);
|
||||
} else {
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -334,6 +334,7 @@ static int idmouse_probe(struct usb_interface *interface,
|
||||
if (dev == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
kref_init(&dev->kref);
|
||||
mutex_init(&dev->lock);
|
||||
dev->udev = udev;
|
||||
dev->interface = interface;
|
||||
@@ -342,8 +343,7 @@ static int idmouse_probe(struct usb_interface *interface,
|
||||
result = usb_find_bulk_in_endpoint(iface_desc, &endpoint);
|
||||
if (result) {
|
||||
dev_err(&interface->dev, "Unable to find bulk-in endpoint.\n");
|
||||
idmouse_delete(dev);
|
||||
return result;
|
||||
goto err_put_kref;
|
||||
}
|
||||
|
||||
dev->orig_bi_size = usb_endpoint_maxp(endpoint);
|
||||
@@ -351,8 +351,8 @@ static int idmouse_probe(struct usb_interface *interface,
|
||||
dev->bulk_in_endpointAddr = endpoint->bEndpointAddress;
|
||||
dev->bulk_in_buffer = kmalloc(IMGSIZE + dev->bulk_in_size, GFP_KERNEL);
|
||||
if (!dev->bulk_in_buffer) {
|
||||
idmouse_delete(dev);
|
||||
return -ENOMEM;
|
||||
result = -ENOMEM;
|
||||
goto err_put_kref;
|
||||
}
|
||||
|
||||
/* allow device read, write and ioctl */
|
||||
@@ -364,14 +364,18 @@ static int idmouse_probe(struct usb_interface *interface,
|
||||
if (result) {
|
||||
/* something prevented us from registering this device */
|
||||
dev_err(&interface->dev, "Unable to allocate minor number.\n");
|
||||
idmouse_delete(dev);
|
||||
return result;
|
||||
goto err_put_kref;
|
||||
}
|
||||
|
||||
/* be noisy */
|
||||
dev_info(&interface->dev,"%s now attached\n",DRIVER_DESC);
|
||||
|
||||
return 0;
|
||||
|
||||
err_put_kref:
|
||||
kref_put(&dev->kref, idmouse_delete);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void idmouse_disconnect(struct usb_interface *interface)
|
||||
@@ -387,14 +391,9 @@ static void idmouse_disconnect(struct usb_interface *interface)
|
||||
/* prevent device read, write and ioctl */
|
||||
dev->present = 0;
|
||||
|
||||
/* if the device is opened, idmouse_release will clean this up */
|
||||
if (!dev->open) {
|
||||
mutex_unlock(&dev->lock);
|
||||
idmouse_delete(dev);
|
||||
} else {
|
||||
/* unlock */
|
||||
mutex_unlock(&dev->lock);
|
||||
}
|
||||
mutex_unlock(&dev->lock);
|
||||
|
||||
kref_put(&dev->kref, idmouse_delete);
|
||||
|
||||
dev_info(&interface->dev, "disconnected\n");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user