summaryrefslogtreecommitdiff
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:57:06 -0700
commitd0bfaacabec9c87b739c31662eaa96af0d1b404c (patch)
treeac6efba33bc2cfb70298c65f3e0154fcd0ef7935
parentb9ea0ce26ce42ee37891ede0ff6c0162005d53cb (diff)
downloadlwn-d0bfaacabec9c87b739c31662eaa96af0d1b404c.tar.gz
lwn-d0bfaacabec9c87b739c31662eaa96af0d1b404c.zip
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>
-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 f77e341631d7..1e10b8b4e453 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -256,9 +256,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);
@@ -789,14 +792,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