aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2013-07-29 00:46:13 -0400
committerRusty Russell <rusty@rustcorp.com.au>2013-07-29 01:13:28 -0400
commit057b82be3ca3d066478e43b162fc082930a746c9 (patch)
tree8ca217f1b7c0e48a21540cb38779cc7a48216535
parent2b4fbf029dff5a28d9bf646346dea891ec43398a (diff)
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>
-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 8a15af3e1a9d..3beea9d478bc 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -272,9 +272,12 @@ static struct port *find_port_by_devt_in_portdev(struct ports_device *portdev,
272 unsigned long flags; 272 unsigned long flags;
273 273
274 spin_lock_irqsave(&portdev->ports_lock, flags); 274 spin_lock_irqsave(&portdev->ports_lock, flags);
275 list_for_each_entry(port, &portdev->ports, list) 275 list_for_each_entry(port, &portdev->ports, list) {
276 if (port->cdev->dev == dev) 276 if (port->cdev->dev == dev) {
277 kref_get(&port->kref);
277 goto out; 278 goto out;
279 }
280 }
278 port = NULL; 281 port = NULL;
279out: 282out:
280 spin_unlock_irqrestore(&portdev->ports_lock, flags); 283 spin_unlock_irqrestore(&portdev->ports_lock, flags);
@@ -1036,14 +1039,10 @@ static int port_fops_open(struct inode *inode, struct file *filp)
1036 struct port *port; 1039 struct port *port;
1037 int ret; 1040 int ret;
1038 1041
1042 /* We get the port with a kref here */
1039 port = find_port_by_devt(cdev->dev); 1043 port = find_port_by_devt(cdev->dev);
1040 filp->private_data = port; 1044 filp->private_data = port;
1041 1045
1042 /* Prevent against a port getting hot-unplugged at the same time */
1043 spin_lock_irq(&port->portdev->ports_lock);
1044 kref_get(&port->kref);
1045 spin_unlock_irq(&port->portdev->ports_lock);
1046
1047 /* 1046 /*
1048 * Don't allow opening of console port devices -- that's done 1047 * Don't allow opening of console port devices -- that's done
1049 * via /dev/hvc 1048 * via /dev/hvc