2012-05-18 11:05:35

by Rabin Vincent

[permalink] [raw]
Subject: Race in vt_event_wait() during suspend/resume

pm_restore_console() is called from the suspend/resume path,
this calls vt_move_to_console(), which calls vt_event_wait().

There's a race in this path which causes the process
which requests the suspend to sleep indefinitely waiting
for an event which already happened:

P1 P2
vt_move_to_console()
set_console()
schedule_console_callback()
vt_waitactive()
check n == fg_console +1
console_callback()
switch_screen()
vt_event_post() // no waiters

vt_event_wait() // forever

I think the following should fix it. Comments?

diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index ede2ef1..1d02e32 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -110,16 +110,7 @@ void vt_event_post(unsigned int event, unsigned
int old, unsigned int new)
wake_up_interruptible(&vt_event_waitqueue);
}

-/**
- * vt_event_wait - wait for an event
- * @vw: our event
- *
- * Waits for an event to occur which completes our vt_event_wait
- * structure. On return the structure has wv->done set to 1 for success
- * or 0 if some event such as a signal ended the wait.
- */
-
-static void vt_event_wait(struct vt_event_wait *vw)
+static void __vt_event_queue(struct vt_event_wait *vw)
{
unsigned long flags;
/* Prepare the event */
@@ -129,8 +120,18 @@ static void vt_event_wait(struct vt_event_wait *vw)
spin_lock_irqsave(&vt_event_lock, flags);
list_add(&vw->list, &vt_events);
spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
+static void __vt_event_wait(struct vt_event_wait *vw)
+{
/* Wait for it to pass */
wait_event_interruptible(vt_event_waitqueue, vw->done);
+}
+
+static void __vt_event_dequeue(struct vt_event_wait *vw)
+{
+ unsigned long flags;
+
/* Dequeue it */
spin_lock_irqsave(&vt_event_lock, flags);
list_del(&vw->list);
@@ -138,6 +139,22 @@ static void vt_event_wait(struct vt_event_wait *vw)
}

/**
+ * vt_event_wait - wait for an event
+ * @vw: our event
+ *
+ * Waits for an event to occur which completes our vt_event_wait
+ * structure. On return the structure has wv->done set to 1 for success
+ * or 0 if some event such as a signal ended the wait.
+ */
+
+static void vt_event_wait(struct vt_event_wait *vw)
+{
+ __vt_event_queue(vw);
+ __vt_event_wait(vw);
+ __vt_event_dequeue(vw);
+}
+
+/**
* vt_event_wait_ioctl - event ioctl handler
* @arg: argument to ioctl
*
@@ -177,10 +194,14 @@ int vt_waitactive(int n)
{
struct vt_event_wait vw;
do {
- if (n == fg_console + 1)
- break;
vw.event.event = VT_EVENT_SWITCH;
- vt_event_wait(&vw);
+ __vt_event_queue(&vw);
+ if (n == fg_console + 1) {
+ __vt_event_dequeue(&vw);
+ break;
+ }
+ __vt_event_wait(&vw);
+ __vt_event_dequeue(&vw);
if (vw.done == 0)
return -EINTR;
} while (vw.event.newev != n);


2012-05-18 11:38:07

by Alan

[permalink] [raw]
Subject: Re: Race in vt_event_wait() during suspend/resume

On Fri, 18 May 2012 16:34:53 +0530
Rabin Vincent <[email protected]> wrote:

> pm_restore_console() is called from the suspend/resume path,
> this calls vt_move_to_console(), which calls vt_event_wait().
>
> There's a race in this path which causes the process
> which requests the suspend to sleep indefinitely waiting
> for an event which already happened:
>
> P1 P2
> vt_move_to_console()
> set_console()
> schedule_console_callback()
> vt_waitactive()
> check n == fg_console +1
> console_callback()
> switch_screen()
> vt_event_post() // no waiters
>
> vt_event_wait() // forever
>
> I think the following should fix it. Comments?
>

Looks right to me.

Alan

2012-05-18 18:06:35

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: Race in vt_event_wait() during suspend/resume

On Fri, May 18, 2012 at 12:40:51PM +0100, Alan Cox wrote:
> On Fri, 18 May 2012 16:34:53 +0530
> Rabin Vincent <[email protected]> wrote:
>
> > pm_restore_console() is called from the suspend/resume path,
> > this calls vt_move_to_console(), which calls vt_event_wait().
> >
> > There's a race in this path which causes the process
> > which requests the suspend to sleep indefinitely waiting
> > for an event which already happened:
> >
> > P1 P2
> > vt_move_to_console()
> > set_console()
> > schedule_console_callback()
> > vt_waitactive()
> > check n == fg_console +1
> > console_callback()
> > switch_screen()
> > vt_event_post() // no waiters
> >
> > vt_event_wait() // forever
> >
> > I think the following should fix it. Comments?
> >
>
> Looks right to me.

Great, Rabin, care to resend it with a proper Signed-off-by: line so
that I can apply it?

thanks,

greg k-h

2012-05-21 08:09:16

by Rabin Vincent

[permalink] [raw]
Subject: [PATCH] vt: fix race in vt_waitactive()

pm_restore_console() is called from the suspend/resume path, and this
calls vt_move_to_console(), which calls vt_waitactive().

There's a race in this path which causes the process which requests the
suspend to sleep indefinitely waiting for an event which already
happened:

P1 P2
vt_move_to_console()
set_console()
schedule_console_callback()
vt_waitactive()
check n == fg_console +1
console_callback()
switch_screen()
vt_event_post() // no waiters

vt_event_wait() // forever

Fix the race by ensuring we're registered for the event before we check
if it's already completed.

Cc: Alan Cox <[email protected]>
Signed-off-by: Rabin Vincent <[email protected]>
---
drivers/tty/vt/vt_ioctl.c | 47 ++++++++++++++++++++++++++++++++------------
1 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
index ede2ef1..1d02e32 100644
--- a/drivers/tty/vt/vt_ioctl.c
+++ b/drivers/tty/vt/vt_ioctl.c
@@ -110,16 +110,7 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
wake_up_interruptible(&vt_event_waitqueue);
}

-/**
- * vt_event_wait - wait for an event
- * @vw: our event
- *
- * Waits for an event to occur which completes our vt_event_wait
- * structure. On return the structure has wv->done set to 1 for success
- * or 0 if some event such as a signal ended the wait.
- */
-
-static void vt_event_wait(struct vt_event_wait *vw)
+static void __vt_event_queue(struct vt_event_wait *vw)
{
unsigned long flags;
/* Prepare the event */
@@ -129,8 +120,18 @@ static void vt_event_wait(struct vt_event_wait *vw)
spin_lock_irqsave(&vt_event_lock, flags);
list_add(&vw->list, &vt_events);
spin_unlock_irqrestore(&vt_event_lock, flags);
+}
+
+static void __vt_event_wait(struct vt_event_wait *vw)
+{
/* Wait for it to pass */
wait_event_interruptible(vt_event_waitqueue, vw->done);
+}
+
+static void __vt_event_dequeue(struct vt_event_wait *vw)
+{
+ unsigned long flags;
+
/* Dequeue it */
spin_lock_irqsave(&vt_event_lock, flags);
list_del(&vw->list);
@@ -138,6 +139,22 @@ static void vt_event_wait(struct vt_event_wait *vw)
}

/**
+ * vt_event_wait - wait for an event
+ * @vw: our event
+ *
+ * Waits for an event to occur which completes our vt_event_wait
+ * structure. On return the structure has wv->done set to 1 for success
+ * or 0 if some event such as a signal ended the wait.
+ */
+
+static void vt_event_wait(struct vt_event_wait *vw)
+{
+ __vt_event_queue(vw);
+ __vt_event_wait(vw);
+ __vt_event_dequeue(vw);
+}
+
+/**
* vt_event_wait_ioctl - event ioctl handler
* @arg: argument to ioctl
*
@@ -177,10 +194,14 @@ int vt_waitactive(int n)
{
struct vt_event_wait vw;
do {
- if (n == fg_console + 1)
- break;
vw.event.event = VT_EVENT_SWITCH;
- vt_event_wait(&vw);
+ __vt_event_queue(&vw);
+ if (n == fg_console + 1) {
+ __vt_event_dequeue(&vw);
+ break;
+ }
+ __vt_event_wait(&vw);
+ __vt_event_dequeue(&vw);
if (vw.done == 0)
return -EINTR;
} while (vw.event.newev != n);
--
1.7.4.3

2012-06-13 23:44:57

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] vt: fix race in vt_waitactive()

On Mon, May 21, 2012 at 01:38:42PM +0530, Rabin Vincent wrote:
> pm_restore_console() is called from the suspend/resume path, and this
> calls vt_move_to_console(), which calls vt_waitactive().
>
> There's a race in this path which causes the process which requests the
> suspend to sleep indefinitely waiting for an event which already
> happened:
>
> P1 P2
> vt_move_to_console()
> set_console()
> schedule_console_callback()
> vt_waitactive()
> check n == fg_console +1
> console_callback()
> switch_screen()
> vt_event_post() // no waiters
>
> vt_event_wait() // forever
>
> Fix the race by ensuring we're registered for the event before we check
> if it's already completed.
>
> Cc: Alan Cox <[email protected]>
> Signed-off-by: Rabin Vincent <[email protected]>
> ---
> drivers/tty/vt/vt_ioctl.c | 47 ++++++++++++++++++++++++++++++++------------
> 1 files changed, 34 insertions(+), 13 deletions(-)

Alan, any thoughts on this patch?

thanks,

greg k-h

>
> diff --git a/drivers/tty/vt/vt_ioctl.c b/drivers/tty/vt/vt_ioctl.c
> index ede2ef1..1d02e32 100644
> --- a/drivers/tty/vt/vt_ioctl.c
> +++ b/drivers/tty/vt/vt_ioctl.c
> @@ -110,16 +110,7 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
> wake_up_interruptible(&vt_event_waitqueue);
> }
>
> -/**
> - * vt_event_wait - wait for an event
> - * @vw: our event
> - *
> - * Waits for an event to occur which completes our vt_event_wait
> - * structure. On return the structure has wv->done set to 1 for success
> - * or 0 if some event such as a signal ended the wait.
> - */
> -
> -static void vt_event_wait(struct vt_event_wait *vw)
> +static void __vt_event_queue(struct vt_event_wait *vw)
> {
> unsigned long flags;
> /* Prepare the event */
> @@ -129,8 +120,18 @@ static void vt_event_wait(struct vt_event_wait *vw)
> spin_lock_irqsave(&vt_event_lock, flags);
> list_add(&vw->list, &vt_events);
> spin_unlock_irqrestore(&vt_event_lock, flags);
> +}
> +
> +static void __vt_event_wait(struct vt_event_wait *vw)
> +{
> /* Wait for it to pass */
> wait_event_interruptible(vt_event_waitqueue, vw->done);
> +}
> +
> +static void __vt_event_dequeue(struct vt_event_wait *vw)
> +{
> + unsigned long flags;
> +
> /* Dequeue it */
> spin_lock_irqsave(&vt_event_lock, flags);
> list_del(&vw->list);
> @@ -138,6 +139,22 @@ static void vt_event_wait(struct vt_event_wait *vw)
> }
>
> /**
> + * vt_event_wait - wait for an event
> + * @vw: our event
> + *
> + * Waits for an event to occur which completes our vt_event_wait
> + * structure. On return the structure has wv->done set to 1 for success
> + * or 0 if some event such as a signal ended the wait.
> + */
> +
> +static void vt_event_wait(struct vt_event_wait *vw)
> +{
> + __vt_event_queue(vw);
> + __vt_event_wait(vw);
> + __vt_event_dequeue(vw);
> +}
> +
> +/**
> * vt_event_wait_ioctl - event ioctl handler
> * @arg: argument to ioctl
> *
> @@ -177,10 +194,14 @@ int vt_waitactive(int n)
> {
> struct vt_event_wait vw;
> do {
> - if (n == fg_console + 1)
> - break;
> vw.event.event = VT_EVENT_SWITCH;
> - vt_event_wait(&vw);
> + __vt_event_queue(&vw);
> + if (n == fg_console + 1) {
> + __vt_event_dequeue(&vw);
> + break;
> + }
> + __vt_event_wait(&vw);
> + __vt_event_dequeue(&vw);
> if (vw.done == 0)
> return -EINTR;
> } while (vw.event.newev != n);
> --
> 1.7.4.3

2012-06-14 09:44:31

by Alan

[permalink] [raw]
Subject: Re: [PATCH] vt: fix race in vt_waitactive()

On Wed, 13 Jun 2012 16:44:52 -0700
Greg KH <[email protected]> wrote:

> On Mon, May 21, 2012 at 01:38:42PM +0530, Rabin Vincent wrote:
> > pm_restore_console() is called from the suspend/resume path, and this
> > calls vt_move_to_console(), which calls vt_waitactive().
> >
> > There's a race in this path which causes the process which requests the
> > suspend to sleep indefinitely waiting for an event which already
> > happened:
> >
> > P1 P2
> > vt_move_to_console()
> > set_console()
> > schedule_console_callback()
> > vt_waitactive()
> > check n == fg_console +1
> > console_callback()
> > switch_screen()
> > vt_event_post() // no waiters
> >
> > vt_event_wait() // forever
> >
> > Fix the race by ensuring we're registered for the event before we check
> > if it's already completed.
> >
> > Cc: Alan Cox <[email protected]>
> > Signed-off-by: Rabin Vincent <[email protected]>
> > ---
> > drivers/tty/vt/vt_ioctl.c | 47 ++++++++++++++++++++++++++++++++------------
> > 1 files changed, 34 insertions(+), 13 deletions(-)
>
> Alan, any thoughts on this patch?

I acked it when first posted - I believe its correct.

2012-06-14 17:20:56

by Greg Kroah-Hartman

[permalink] [raw]
Subject: Re: [PATCH] vt: fix race in vt_waitactive()

On Thu, Jun 14, 2012 at 10:48:02AM +0100, Alan Cox wrote:
> On Wed, 13 Jun 2012 16:44:52 -0700
> Greg KH <[email protected]> wrote:
>
> > On Mon, May 21, 2012 at 01:38:42PM +0530, Rabin Vincent wrote:
> > > pm_restore_console() is called from the suspend/resume path, and this
> > > calls vt_move_to_console(), which calls vt_waitactive().
> > >
> > > There's a race in this path which causes the process which requests the
> > > suspend to sleep indefinitely waiting for an event which already
> > > happened:
> > >
> > > P1 P2
> > > vt_move_to_console()
> > > set_console()
> > > schedule_console_callback()
> > > vt_waitactive()
> > > check n == fg_console +1
> > > console_callback()
> > > switch_screen()
> > > vt_event_post() // no waiters
> > >
> > > vt_event_wait() // forever
> > >
> > > Fix the race by ensuring we're registered for the event before we check
> > > if it's already completed.
> > >
> > > Cc: Alan Cox <[email protected]>
> > > Signed-off-by: Rabin Vincent <[email protected]>
> > > ---
> > > drivers/tty/vt/vt_ioctl.c | 47 ++++++++++++++++++++++++++++++++------------
> > > 1 files changed, 34 insertions(+), 13 deletions(-)
> >
> > Alan, any thoughts on this patch?
>
> I acked it when first posted - I believe its correct.

Ah, sorry, I missed that somehow. I'll go queue it up now.

greg k-h