]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
KVM: arm64: Don't eagerly teardown the vgic on init error
authorMarc Zyngier <maz@kernel.org>
Wed, 9 Oct 2024 18:36:03 +0000 (19:36 +0100)
committerMarc Zyngier <maz@kernel.org>
Fri, 11 Oct 2024 12:40:25 +0000 (13:40 +0100)
As there is very little ordering in the KVM API, userspace can
instanciate a half-baked GIC (missing its memory map, for example)
at almost any time.

This means that, with the right timing, a thread running vcpu-0
can enter the kernel without a GIC configured and get a GIC created
behind its back by another thread. Amusingly, it will pick up
that GIC and start messing with the data structures without the
GIC having been fully initialised.

Similarly, a thread running vcpu-1 can enter the kernel, and try
to init the GIC that was previously created. Since this GIC isn't
properly configured (no memory map), it fails to correctly initialise.

And that's the point where we decide to teardown the GIC, freeing all
its resources. Behind vcpu-0's back. Things stop pretty abruptly,
with a variety of symptoms.  Clearly, this isn't good, we should be
a bit more careful about this.

It is obvious that this guest is not viable, as it is missing some
important part of its configuration. So instead of trying to tear
bits of it down, let's just mark it as *dead*. It means that any
further interaction from userspace will result in -EIO. The memory
will be released on the "normal" path, when userspace gives up.

Cc: stable@vger.kernel.org
Reported-by: Alexander Potapenko <glider@google.com>
Reviewed-by: Oliver Upton <oliver.upton@linux.dev>
Link: https://lore.kernel.org/r/20241009183603.3221824-1-maz@kernel.org
Signed-off-by: Marc Zyngier <maz@kernel.org>
arch/arm64/kvm/arm.c
arch/arm64/kvm/vgic/vgic-init.c

index c7b659604baeaf6cb31e60d0a4c1fd73591aed20..48cafb65d6acff5610da0851633865697ba9a132 100644 (file)
@@ -997,6 +997,9 @@ static int kvm_vcpu_suspend(struct kvm_vcpu *vcpu)
 static int check_vcpu_requests(struct kvm_vcpu *vcpu)
 {
        if (kvm_request_pending(vcpu)) {
+               if (kvm_check_request(KVM_REQ_VM_DEAD, vcpu))
+                       return -EIO;
+
                if (kvm_check_request(KVM_REQ_SLEEP, vcpu))
                        kvm_vcpu_sleep(vcpu);
 
index 57aedf7b2b763f9b5dc012c76c25f68af0f97f8f..d88fdeaf6144b72675632768f0a48939ee7038f1 100644 (file)
@@ -556,10 +556,10 @@ int kvm_vgic_map_resources(struct kvm *kvm)
 out:
        mutex_unlock(&kvm->arch.config_lock);
 out_slots:
-       mutex_unlock(&kvm->slots_lock);
-
        if (ret)
-               kvm_vgic_destroy(kvm);
+               kvm_vm_dead(kvm);
+
+       mutex_unlock(&kvm->slots_lock);
 
        return ret;
 }