summaryrefslogtreecommitdiffstats
path: root/drivers/usb/class
diff options
context:
space:
mode:
authorHavard Skinnemoen <hskinnemoen@google.com>2011-11-09 22:47:38 +0100
committerGreg Kroah-Hartman <gregkh@suse.de>2011-11-14 22:47:49 +0100
commit5dc2470c602da8851907ec18942cd876c3b4ecc1 (patch)
treea6ef0bac36a5d207ebb9cdfe3e5285bba0d53e40 /drivers/usb/class
parentUSB: add quirk for Logitech C600 web cam (diff)
downloadlinux-5dc2470c602da8851907ec18942cd876c3b4ecc1.tar.xz
linux-5dc2470c602da8851907ec18942cd876c3b4ecc1.zip
USB: cdc-acm: Fix disconnect() vs close() race
There's a race between the USB disconnect handler and the TTY close handler which may cause the acm object to be freed while it's still being used. This may lead to things like http://article.gmane.org/gmane.linux.usb.general/54250 and https://lkml.org/lkml/2011/5/29/64 This is the simplest fix I could come up with. Holding on to open_mutex while closing the TTY device prevents acm_disconnect() from freeing the acm object between acm->port.count drops to 0 and the TTY side of the cleanups are finalized. Signed-off-by: Havard Skinnemoen <hskinnemoen@google.com> Cc: Oliver Neukum <oliver@neukum.name> Cc: stable <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/cdc-acm.c8
1 files changed, 5 insertions, 3 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
index 6960715c5063..e8c564a53346 100644
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -539,7 +539,6 @@ static void acm_port_down(struct acm *acm)
{
int i;
- mutex_lock(&open_mutex);
if (acm->dev) {
usb_autopm_get_interface(acm->control);
acm_set_control(acm, acm->ctrlout = 0);
@@ -551,14 +550,15 @@ static void acm_port_down(struct acm *acm)
acm->control->needs_remote_wakeup = 0;
usb_autopm_put_interface(acm->control);
}
- mutex_unlock(&open_mutex);
}
static void acm_tty_hangup(struct tty_struct *tty)
{
struct acm *acm = tty->driver_data;
tty_port_hangup(&acm->port);
+ mutex_lock(&open_mutex);
acm_port_down(acm);
+ mutex_unlock(&open_mutex);
}
static void acm_tty_close(struct tty_struct *tty, struct file *filp)
@@ -569,8 +569,9 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
shutdown */
if (!acm)
return;
+
+ mutex_lock(&open_mutex);
if (tty_port_close_start(&acm->port, tty, filp) == 0) {
- mutex_lock(&open_mutex);
if (!acm->dev) {
tty_port_tty_set(&acm->port, NULL);
acm_tty_unregister(acm);
@@ -582,6 +583,7 @@ static void acm_tty_close(struct tty_struct *tty, struct file *filp)
acm_port_down(acm);
tty_port_close_end(&acm->port, tty);
tty_port_tty_set(&acm->port, NULL);
+ mutex_unlock(&open_mutex);
}
static int acm_tty_write(struct tty_struct *tty,