aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2013-07-29 14:16:13 +0930
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-08-14 22:55:16 -0700
commite3a5a432070dcebaffee8f9af193f99471280ea9 (patch)
treecc41564cc5ada0a14174ac1aa88357c822c08e05 /drivers/char
parent2e18e5161c634aa5d7e685f348aca98d295accb8 (diff)
downloadkernel_samsung_tuna-e3a5a432070dcebaffee8f9af193f99471280ea9.zip
kernel_samsung_tuna-e3a5a432070dcebaffee8f9af193f99471280ea9.tar.gz
kernel_samsung_tuna-e3a5a432070dcebaffee8f9af193f99471280ea9.tar.bz2
virtio: console: fix race with port unplug and open/close
commit 057b82be3ca3d066478e43b162fc082930a746c9 upstream. There's a window between find_port_by_devt() returning a port and us taking a kref on the port, where the port could get unplugged. Fix it by taking the reference in find_port_by_devt() itself. Problem reported and analyzed by Mateusz Guzik. Reported-by: Mateusz Guzik <mguzik@redhat.com> Signed-off-by: Amit Shah <amit.shah@redhat.com> Signed-off-by: Rusty Russell <rusty@rustcorp.com.au> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/virtio_console.c13
1 files changed, 6 insertions, 7 deletions
diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c
index a95256a..a21d6d2 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -241,9 +241,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
unsigned long flags;
spin_lock_irqsave(&portdev->ports_lock, flags);
- list_for_each_entry(port, &portdev->ports, list)
- if (port->cdev->dev == dev)
+ list_for_each_entry(port, &portdev->ports, list) {
+ if (port->cdev->dev == dev) {
+ kref_get(&port->kref);
goto out;
+ }
+ }
port = NULL;
out:
spin_unlock_irqrestore(&portdev->ports_lock, flags);
@@ -781,14 +784,10 @@ static int port_fops_open(struct inode *inode, struct file *filp)
struct port *port;
int ret;
+ /* We get the port with a kref here */
port = find_port_by_devt(cdev->dev);
filp->private_data = port;
- /* Prevent against a port getting hot-unplugged at the same time */
- spin_lock_irq(&port->portdev->ports_lock);
- kref_get(&port->kref);
- spin_unlock_irq(&port->portdev->ports_lock);
-
/*
* Don't allow opening of console port devices -- that's done
* via /dev/hvc