Commit a7b12929be6cc55eab2dac3330fa9f5984e12dda

Authored by Rabin Vincent
Committed by Greg Kroah-Hartman
1 parent ed85c60457

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: Rabin Vincent <rabin.vincent@stericsson.com>
Acked-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 1 changed file with 34 additions and 13 deletions Side-by-side Diff

drivers/tty/vt/vt_ioctl.c
... ... @@ -110,16 +110,7 @@
110 110 wake_up_interruptible(&vt_event_waitqueue);
111 111 }
112 112  
113   -/**
114   - * vt_event_wait - wait for an event
115   - * @vw: our event
116   - *
117   - * Waits for an event to occur which completes our vt_event_wait
118   - * structure. On return the structure has wv->done set to 1 for success
119   - * or 0 if some event such as a signal ended the wait.
120   - */
121   -
122   -static void vt_event_wait(struct vt_event_wait *vw)
  113 +static void __vt_event_queue(struct vt_event_wait *vw)
123 114 {
124 115 unsigned long flags;
125 116 /* Prepare the event */
126 117  
... ... @@ -129,8 +120,18 @@
129 120 spin_lock_irqsave(&vt_event_lock, flags);
130 121 list_add(&vw->list, &vt_events);
131 122 spin_unlock_irqrestore(&vt_event_lock, flags);
  123 +}
  124 +
  125 +static void __vt_event_wait(struct vt_event_wait *vw)
  126 +{
132 127 /* Wait for it to pass */
133 128 wait_event_interruptible(vt_event_waitqueue, vw->done);
  129 +}
  130 +
  131 +static void __vt_event_dequeue(struct vt_event_wait *vw)
  132 +{
  133 + unsigned long flags;
  134 +
134 135 /* Dequeue it */
135 136 spin_lock_irqsave(&vt_event_lock, flags);
136 137 list_del(&vw->list);
... ... @@ -138,6 +139,22 @@
138 139 }
139 140  
140 141 /**
  142 + * vt_event_wait - wait for an event
  143 + * @vw: our event
  144 + *
  145 + * Waits for an event to occur which completes our vt_event_wait
  146 + * structure. On return the structure has wv->done set to 1 for success
  147 + * or 0 if some event such as a signal ended the wait.
  148 + */
  149 +
  150 +static void vt_event_wait(struct vt_event_wait *vw)
  151 +{
  152 + __vt_event_queue(vw);
  153 + __vt_event_wait(vw);
  154 + __vt_event_dequeue(vw);
  155 +}
  156 +
  157 +/**
141 158 * vt_event_wait_ioctl - event ioctl handler
142 159 * @arg: argument to ioctl
143 160 *
144 161  
... ... @@ -177,10 +194,14 @@
177 194 {
178 195 struct vt_event_wait vw;
179 196 do {
180   - if (n == fg_console + 1)
181   - break;
182 197 vw.event.event = VT_EVENT_SWITCH;
183   - vt_event_wait(&vw);
  198 + __vt_event_queue(&vw);
  199 + if (n == fg_console + 1) {
  200 + __vt_event_dequeue(&vw);
  201 + break;
  202 + }
  203 + __vt_event_wait(&vw);
  204 + __vt_event_dequeue(&vw);
184 205 if (vw.done == 0)
185 206 return -EINTR;
186 207 } while (vw.event.newev != n);