vgic_init() compares created_vcpus with online_vcpus to detect a vCPU
being created. However, CTRL_INIT only holds config_lock, while
admission of new vCPUs and updates to created_vcpus are protected by
kvm->lock.
A concurrent KVM_CREATE_VCPU can therefore increment created_vcpus
after vgic_init() checks the counters, then block on config_lock before
publishing the new vCPU. For a GICv4 VM, vgic_v4_init() consequently
sizes its vPE array using a stale online_vcpus value, leaving no vPE
entry for the new vCPU once its creation completes. The upcoming GICv5
IRS support would hit the same issue when sizing its doorbell domain.
Hold kvm->lock around CTRL_INIT. The existing counter check detects a
vCPU creation already in progress, while the lock prevents a new one
from starting until initialisation has completed.
Fixes: f00327731131 ("KVM: arm64: Use config_lock to protect vgic state")
Reported-by: Sashiko <sashiko-bot@kernel.org>
Link: https://lore.kernel.org/r/20260807124506.ED85C1F000E9@smtp.kernel.org (local)
Signed-off-by: Sascha Bischoff <redacted>
---
arch/arm64/kvm/vgic/vgic-kvm-device.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c
index 90be99443df3b..219881915bf41 100644
--- a/arch/arm64/kvm/vgic/vgic-kvm-device.c
+++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c
@@ -255,9 +255,11 @@ static int vgic_set_common_attr(struct kvm_device *dev,
case KVM_DEV_ARM_VGIC_GRP_CTRL: {
switch (attr->attr) {
case KVM_DEV_ARM_VGIC_CTRL_INIT:
+ mutex_lock(&dev->kvm->lock);
mutex_lock(&dev->kvm->arch.config_lock);
r = vgic_init(dev->kvm);
mutex_unlock(&dev->kvm->arch.config_lock);
+ mutex_unlock(&dev->kvm->lock);
return r;
case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES:
/*--
2.34.1