virtio: console: fix race with port unplug and open/close

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.

CC: <stable@vger.kernel.org>
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>
This commit is contained in:
Amit Shah 2013-07-29 14:16:13 +09:30 committed by Rusty Russell
parent 2b4fbf029d
commit 057b82be3c
1 changed files with 6 additions and 7 deletions

View File

@ -272,9 +272,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&portdev->ports_lock, flags); spin_lock_irqsave(&portdev->ports_lock, flags);
list_for_each_entry(port, &portdev->ports, list) list_for_each_entry(port, &portdev->ports, list) {
if (port->cdev->dev == dev) if (port->cdev->dev == dev) {
kref_get(&port->kref);
goto out; goto out;
}
}
port = NULL; port = NULL;
out: out:
spin_unlock_irqrestore(&portdev->ports_lock, flags); spin_unlock_irqrestore(&portdev->ports_lock, flags);
@ -1036,14 +1039,10 @@ static int port_fops_open(struct inode *inode, struct file *filp)
struct port *port; struct port *port;
int ret; int ret;
/* We get the port with a kref here */
port = find_port_by_devt(cdev->dev); port = find_port_by_devt(cdev->dev);
filp->private_data = port; 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 * Don't allow opening of console port devices -- that's done
* via /dev/hvc * via /dev/hvc