diff options
author | Amit Shah <amit.shah@redhat.com> | 2013-07-29 00:46:13 -0400 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2013-07-29 01:13:28 -0400 |
commit | 057b82be3ca3d066478e43b162fc082930a746c9 (patch) | |
tree | 8ca217f1b7c0e48a21540cb38779cc7a48216535 | |
parent | 2b4fbf029dff5a28d9bf646346dea891ec43398a (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.c | 13 |
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; |
279 | out: | 282 | out: |
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 |