Rusty Russell
2013-Jul-22 05:45 UTC
[PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
Amit Shah <amit.shah at redhat.com> writes:> On (Fri) 19 Jul 2013 [18:17:32], Jason Wang wrote: >> On 07/19/2013 03:48 PM, Amit Shah wrote: >> > On (Fri) 19 Jul 2013 [15:03:50], Jason Wang wrote: >> >> On 07/19/2013 04:16 AM, Amit Shah wrote: >> >>> Between poll() being called and processed, the port can be unplugged. >> >>> Check if this happened, and bail out. >> >>> >> >>> Signed-off-by: Amit Shah <amit.shah at redhat.com> >> >>> --- >> >>> drivers/char/virtio_console.c | 4 ++++ >> >>> 1 file changed, 4 insertions(+) >> >>> >> >>> diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c >> >>> index 7728af9..1d4b748 100644 >> >>> --- a/drivers/char/virtio_console.c >> >>> +++ b/drivers/char/virtio_console.c >> >>> @@ -967,6 +967,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) >> >>> unsigned int ret; >> >>> >> >>> port = filp->private_data; >> >>> + if (!port->guest_connected) { >> >>> + /* Port was unplugged before we could proceed */ >> >>> + return POLLHUP; >> >>> + } >> >>> poll_wait(filp, &port->waitqueue, wait); >> >>> >> >>> if (!port->guest_connected) { >> >> Looks still racy here. Unlike port_fops_read() which check >> >> will_read_block(). If unplug happens after the check but before the >> >> poll_wait(), caller will be blocked forever. >> > unplug_port() calls wake_up_interruptible on the waitqueue. >> >> I mean the following cases: > > (formatting to fit properly:) > >> >> CPU0: CPU1: unplug_port() >> >> if (!port->guest_connected) { >> return POLLHUP; >> } >> wake_up_interruptiable() >> >> poll_wait(filp, &port->waitqueue, wait); > > Agreed, this can happen. I can't think of a way to resolve this. One > way would be to remove the waitqueue (port->waitqueue = NULL in > unplug_port()), but I'm not sure of the effect on the other parts > yet. I'll leave this one for later analysis.No, you are confused by the name, I think, poll_wait() doesn't actually wait. It's more like a poll_enqueue(). Cheers, Rusty.
Jason Wang
2013-Jul-23 03:01 UTC
[PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
On 07/22/2013 01:45 PM, Rusty Russell wrote:> Amit Shah <amit.shah at redhat.com> writes: >> On (Fri) 19 Jul 2013 [18:17:32], Jason Wang wrote: >>> On 07/19/2013 03:48 PM, Amit Shah wrote: >>>> On (Fri) 19 Jul 2013 [15:03:50], Jason Wang wrote: >>>>> On 07/19/2013 04:16 AM, Amit Shah wrote: >>>>>> Between poll() being called and processed, the port can be unplugged. >>>>>> Check if this happened, and bail out. >>>>>> >>>>>> Signed-off-by: Amit Shah <amit.shah at redhat.com> >>>>>> --- >>>>>> drivers/char/virtio_console.c | 4 ++++ >>>>>> 1 file changed, 4 insertions(+) >>>>>> >>>>>> diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c >>>>>> index 7728af9..1d4b748 100644 >>>>>> --- a/drivers/char/virtio_console.c >>>>>> +++ b/drivers/char/virtio_console.c >>>>>> @@ -967,6 +967,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) >>>>>> unsigned int ret; >>>>>> >>>>>> port = filp->private_data; >>>>>> + if (!port->guest_connected) { >>>>>> + /* Port was unplugged before we could proceed */ >>>>>> + return POLLHUP; >>>>>> + } >>>>>> poll_wait(filp, &port->waitqueue, wait); >>>>>> >>>>>> if (!port->guest_connected) { >>>>> Looks still racy here. Unlike port_fops_read() which check >>>>> will_read_block(). If unplug happens after the check but before the >>>>> poll_wait(), caller will be blocked forever. >>>> unplug_port() calls wake_up_interruptible on the waitqueue. >>> I mean the following cases: >> (formatting to fit properly:) >> >>> CPU0: CPU1: unplug_port() >>> >>> if (!port->guest_connected) { >>> return POLLHUP; >>> } >>> wake_up_interruptiable() >>> >>> poll_wait(filp, &port->waitqueue, wait); >> Agreed, this can happen. I can't think of a way to resolve this. One >> way would be to remove the waitqueue (port->waitqueue = NULL in >> unplug_port()), but I'm not sure of the effect on the other parts >> yet. I'll leave this one for later analysis. > No, you are confused by the name, I think, > > poll_wait() doesn't actually wait. It's more like a poll_enqueue().Yes, but the caller will wait then and since the wakeup was called before adding into waitqueue. It may block forever?> > Cheers, > Rusty.
Rusty Russell
2013-Jul-23 05:26 UTC
[PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
Jason Wang <jasowang at redhat.com> writes:> On 07/22/2013 01:45 PM, Rusty Russell wrote: >> Amit Shah <amit.shah at redhat.com> writes: >>> On (Fri) 19 Jul 2013 [18:17:32], Jason Wang wrote: >>>> On 07/19/2013 03:48 PM, Amit Shah wrote: >>>>> On (Fri) 19 Jul 2013 [15:03:50], Jason Wang wrote: >>>>>> On 07/19/2013 04:16 AM, Amit Shah wrote: >>>>>>> Between poll() being called and processed, the port can be unplugged. >>>>>>> Check if this happened, and bail out. >>>>>>> >>>>>>> Signed-off-by: Amit Shah <amit.shah at redhat.com> >>>>>>> --- >>>>>>> drivers/char/virtio_console.c | 4 ++++ >>>>>>> 1 file changed, 4 insertions(+) >>>>>>> >>>>>>> diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c >>>>>>> index 7728af9..1d4b748 100644 >>>>>>> --- a/drivers/char/virtio_console.c >>>>>>> +++ b/drivers/char/virtio_console.c >>>>>>> @@ -967,6 +967,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) >>>>>>> unsigned int ret; >>>>>>> >>>>>>> port = filp->private_data; >>>>>>> + if (!port->guest_connected) { >>>>>>> + /* Port was unplugged before we could proceed */ >>>>>>> + return POLLHUP; >>>>>>> + } >>>>>>> poll_wait(filp, &port->waitqueue, wait); >>>>>>> >>>>>>> if (!port->guest_connected) { >>>>>> Looks still racy here. Unlike port_fops_read() which check >>>>>> will_read_block(). If unplug happens after the check but before the >>>>>> poll_wait(), caller will be blocked forever. >>>>> unplug_port() calls wake_up_interruptible on the waitqueue. >>>> I mean the following cases: >>> (formatting to fit properly:) >>> >>>> CPU0: CPU1: unplug_port() >>>> >>>> if (!port->guest_connected) { >>>> return POLLHUP; >>>> } >>>> wake_up_interruptiable() >>>> >>>> poll_wait(filp, &port->waitqueue, wait); >>> Agreed, this can happen. I can't think of a way to resolve this. One >>> way would be to remove the waitqueue (port->waitqueue = NULL in >>> unplug_port()), but I'm not sure of the effect on the other parts >>> yet. I'll leave this one for later analysis. >> No, you are confused by the name, I think, >> >> poll_wait() doesn't actually wait. It's more like a poll_enqueue(). > > Yes, but the caller will wait then and since the wakeup was called > before adding into waitqueue. It may block forever?No, we enqueue then check: port = filp->private_data; poll_wait(filp, &port->waitqueue, wait); if (!port->guest_connected) { /* Port got unplugged */ return POLLHUP; } ret = 0; if (!will_read_block(port)) ret |= POLLIN | POLLRDNORM; if (!will_write_block(port)) ret |= POLLOUT; if (!port->host_connected) ret |= POLLHUP; return ret; Which is the correct way to do this. Cheers, Rusty.
Amit Shah
2013-Jul-23 08:08 UTC
[PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
On (Mon) 22 Jul 2013 [15:15:34], Rusty Russell wrote:> Amit Shah <amit.shah at redhat.com> writes: > > On (Fri) 19 Jul 2013 [18:17:32], Jason Wang wrote: > >> On 07/19/2013 03:48 PM, Amit Shah wrote: > >> > On (Fri) 19 Jul 2013 [15:03:50], Jason Wang wrote: > >> >> On 07/19/2013 04:16 AM, Amit Shah wrote: > >> >>> Between poll() being called and processed, the port can be unplugged. > >> >>> Check if this happened, and bail out. > >> >>> > >> >>> Signed-off-by: Amit Shah <amit.shah at redhat.com> > >> >>> --- > >> >>> drivers/char/virtio_console.c | 4 ++++ > >> >>> 1 file changed, 4 insertions(+) > >> >>> > >> >>> diff --git a/drivers/char/virtio_console.c b/drivers/char/virtio_console.c > >> >>> index 7728af9..1d4b748 100644 > >> >>> --- a/drivers/char/virtio_console.c > >> >>> +++ b/drivers/char/virtio_console.c > >> >>> @@ -967,6 +967,10 @@ static unsigned int port_fops_poll(struct file *filp, poll_table *wait) > >> >>> unsigned int ret; > >> >>> > >> >>> port = filp->private_data; > >> >>> + if (!port->guest_connected) { > >> >>> + /* Port was unplugged before we could proceed */ > >> >>> + return POLLHUP; > >> >>> + } > >> >>> poll_wait(filp, &port->waitqueue, wait); > >> >>> > >> >>> if (!port->guest_connected) { > >> >> Looks still racy here. Unlike port_fops_read() which check > >> >> will_read_block(). If unplug happens after the check but before the > >> >> poll_wait(), caller will be blocked forever. > >> > unplug_port() calls wake_up_interruptible on the waitqueue. > >> > >> I mean the following cases: > > > > (formatting to fit properly:) > > > >> > >> CPU0: CPU1: unplug_port() > >> > >> if (!port->guest_connected) { > >> return POLLHUP; > >> } > >> wake_up_interruptiable() > >> > >> poll_wait(filp, &port->waitqueue, wait); > > > > Agreed, this can happen. I can't think of a way to resolve this. One > > way would be to remove the waitqueue (port->waitqueue = NULL in > > unplug_port()), but I'm not sure of the effect on the other parts > > yet. I'll leave this one for later analysis. > > No, you are confused by the name, I think, > > poll_wait() doesn't actually wait. It's more like a poll_enqueue().Ah! Thanks, yes. I'll drop this patch. Amit
Possibly Parallel Threads
- [PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
- [PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
- [PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
- [PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug
- [PATCH 06/10] virtio: console: fix race in port_fops_poll() and port unplug