KVM: Fix kvm_vcpu_block() task state race
Marcelo Tosatti [Thu, 8 May 2008 22:47:01 +0000 (19:47 -0300)]
There's still a race in kvm_vcpu_block(), if a wake_up_interruptible()
call happens before the task state is set to TASK_INTERRUPTIBLE:

CPU0                            CPU1

kvm_vcpu_block

add_wait_queue

kvm_cpu_has_interrupt = 0
                                set interrupt
                                if (waitqueue_active())
                                        wake_up_interruptible()

kvm_cpu_has_pending_timer
kvm_arch_vcpu_runnable
signal_pending

set_current_state(TASK_INTERRUPTIBLE)
schedule()

Can be fixed by using prepare_to_wait() which sets the task state before
testing for the wait condition.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Signed-off-by: Avi Kivity <avi@qumranet.com>

virt/kvm/kvm_main.c

index f7ba099..2d29e26 100644 (file)
@@ -758,25 +758,26 @@ void mark_page_dirty(struct kvm *kvm, gfn_t gfn)
  */
 void kvm_vcpu_block(struct kvm_vcpu *vcpu)
 {
-       DECLARE_WAITQUEUE(wait, current);
-
-       add_wait_queue(&vcpu->wq, &wait);
-
-       /*
-        * We will block until either an interrupt or a signal wakes us up
-        */
-       while (!kvm_cpu_has_interrupt(vcpu)
-              && !kvm_cpu_has_pending_timer(vcpu)
-              && !signal_pending(current)
-              && !kvm_arch_vcpu_runnable(vcpu)) {
-               set_current_state(TASK_INTERRUPTIBLE);
+       DEFINE_WAIT(wait);
+
+       for (;;) {
+               prepare_to_wait(&vcpu->wq, &wait, TASK_INTERRUPTIBLE);
+
+               if (kvm_cpu_has_interrupt(vcpu))
+                       break;
+               if (kvm_cpu_has_pending_timer(vcpu))
+                       break;
+               if (kvm_arch_vcpu_runnable(vcpu))
+                       break;
+               if (signal_pending(current))
+                       break;
+
                vcpu_put(vcpu);
                schedule();
                vcpu_load(vcpu);
        }
 
-       __set_current_state(TASK_RUNNING);
-       remove_wait_queue(&vcpu->wq, &wait);
+       finish_wait(&vcpu->wq, &wait);
 }
 
 void kvm_resched(struct kvm_vcpu *vcpu)