Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2fc46915 authored by Rabin Vincent's avatar Rabin Vincent Committed by Greg Kroah-Hartman
Browse files

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.

Signed-off-by: default avatarRabin Vincent <rabin.vincent@stericsson.com>
Acked-by: default avatarAlan Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 057eb856
Loading
Loading
Loading
Loading
+34 −13
Original line number Original line Diff line number Diff line
@@ -110,16 +110,7 @@ void vt_event_post(unsigned int event, unsigned int old, unsigned int new)
		wake_up_interruptible(&vt_event_waitqueue);
		wake_up_interruptible(&vt_event_waitqueue);
}
}


/**
static void __vt_event_queue(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)
{
{
	unsigned long flags;
	unsigned long flags;
	/* Prepare the event */
	/* Prepare the event */
@@ -129,14 +120,40 @@ static void vt_event_wait(struct vt_event_wait *vw)
	spin_lock_irqsave(&vt_event_lock, flags);
	spin_lock_irqsave(&vt_event_lock, flags);
	list_add(&vw->list, &vt_events);
	list_add(&vw->list, &vt_events);
	spin_unlock_irqrestore(&vt_event_lock, flags);
	spin_unlock_irqrestore(&vt_event_lock, flags);
}

static void __vt_event_wait(struct vt_event_wait *vw)
{
	/* Wait for it to pass */
	/* Wait for it to pass */
	wait_event_interruptible(vt_event_waitqueue, vw->done);
	wait_event_interruptible(vt_event_waitqueue, vw->done);
}

static void __vt_event_dequeue(struct vt_event_wait *vw)
{
	unsigned long flags;

	/* Dequeue it */
	/* Dequeue it */
	spin_lock_irqsave(&vt_event_lock, flags);
	spin_lock_irqsave(&vt_event_lock, flags);
	list_del(&vw->list);
	list_del(&vw->list);
	spin_unlock_irqrestore(&vt_event_lock, flags);
	spin_unlock_irqrestore(&vt_event_lock, flags);
}
}


/**
 *	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
 *	vt_event_wait_ioctl	-	event ioctl handler
 *	@arg: argument to ioctl
 *	@arg: argument to ioctl
@@ -177,10 +194,14 @@ int vt_waitactive(int n)
{
{
	struct vt_event_wait vw;
	struct vt_event_wait vw;
	do {
	do {
		if (n == fg_console + 1)
			break;
		vw.event.event = VT_EVENT_SWITCH;
		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)
		if (vw.done == 0)
			return -EINTR;
			return -EINTR;
	} while (vw.event.newev != n);
	} while (vw.event.newev != n);