]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
KVM: Verify there's at least one online vCPU when iterating over all vCPUs
authorSean Christopherson <seanjc@google.com>
Wed, 9 Oct 2024 15:04:51 +0000 (08:04 -0700)
committerSean Christopherson <seanjc@google.com>
Mon, 16 Dec 2024 22:37:30 +0000 (14:37 -0800)
Explicitly check that there is at least online vCPU before iterating over
all vCPUs.  Because the max index is an unsigned long, passing "0 - 1" in
the online_vcpus==0 case results in xa_for_each_range() using an unlimited
max, i.e. allows it to access vCPU0 when it shouldn't.  This will allow
KVM to safely _erase_ from vcpu_array if the last stages of vCPU creation
fail, i.e. without generating a use-after-free if a different task happens
to be concurrently iterating over all vCPUs.

Note, because xa_for_each_range() is a macro, kvm_for_each_vcpu() subtly
reloads online_vcpus after each iteration, i.e. adding an extra load
doesn't meaningfully impact the total cost of iterating over all vCPUs.
And because online_vcpus is never decremented, there is no risk of a
reload triggering a walk of the entire xarray.

Cc: Will Deacon <will@kernel.org>
Cc: Michal Luczaj <mhal@rbox.co>
Acked-by: Will Deacon <will@kernel.org>
Link: https://lore.kernel.org/r/20241009150455.1057573-3-seanjc@google.com
Signed-off-by: Sean Christopherson <seanjc@google.com>
include/linux/kvm_host.h

index b0b38744c4b0559566fc3f4558cc1433de8df5dd..92f192bec07b6bd39b3782c8e58b10834a519a22 100644 (file)
@@ -979,9 +979,10 @@ static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i)
        return xa_load(&kvm->vcpu_array, i);
 }
 
-#define kvm_for_each_vcpu(idx, vcpup, kvm)                \
-       xa_for_each_range(&kvm->vcpu_array, idx, vcpup, 0, \
-                         (atomic_read(&kvm->online_vcpus) - 1))
+#define kvm_for_each_vcpu(idx, vcpup, kvm)                             \
+       if (atomic_read(&kvm->online_vcpus))                            \
+               xa_for_each_range(&kvm->vcpu_array, idx, vcpup, 0,      \
+                                 (atomic_read(&kvm->online_vcpus) - 1))
 
 static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id)
 {