aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorAmit Shah <amit.shah@redhat.com>2013-07-29 14:16:13 +0930
committerBen Hutchings <ben@decadent.org.uk>2013-09-10 01:57:16 +0100
commit0426ea8291c0cdcf0d585c39a7bfae18e621d93b (patch)
tree5fb067ca4bef0cb94d6e7610b6a07804f9e68f2e /drivers/char
parent090f5ec2de3025a9d6d0f65ff0b535c5bac01fa8 (diff)
downloadkernel_samsung_smdk4412-0426ea8291c0cdcf0d585c39a7bfae18e621d93b.zip
kernel_samsung_smdk4412-0426ea8291c0cdcf0d585c39a7bfae18e621d93b.tar.gz
kernel_samsung_smdk4412-0426ea8291c0cdcf0d585c39a7bfae18e621d93b.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: Ben Hutchings <ben@decadent.org.uk>
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 d5ae736..5746a36 100644
--- a/drivers/char/virtio_console.c
+++ b/drivers/char/virtio_console.c
@@ -257,9 +257,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);
@@ -793,14 +796,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