[PATCH] arm64: KVM: Initialize vGIC before preempt-disabled section in kvm_reset_vcpu()
From: Deepanshu Kartikey
Date: Sun Apr 12 2026 - 04:04:58 EST
kvm_reset_vcpu() calls kvm_timer_vcpu_reset() inside a preempt-disabled
section to avoid races with preempt notifiers that also call vcpu put/load.
However, kvm_timer_vcpu_reset() eventually calls kvm_vgic_inject_irq()
which triggers vgic_lazy_init() if the vGIC has not been initialized yet.
vgic_lazy_init() acquires a mutex and calls vgic_init() which invokes
synchronize_srcu_expedited() -- both of which may sleep. Sleeping inside
a preempt-disabled section is illegal and causes:
BUG: scheduling while atomic: syz.1.49/3699/0x00000002
Fix this by calling vgic_lazy_init() before preempt_disable(). On the
second call inside kvm_vgic_inject_irq(), vgic_initialized() will return
true and vgic_lazy_init() will return immediately without sleeping.
Fixes: e761a927bc9a ("KVM: arm/arm64: Reset the VCPU without preemption and vcpu state loaded")
Reported-by: syzbot+12b178b7c756664d2518@xxxxxxxxxxxxxxxxxxxxxxxxx
Closes: https://syzkaller.appspot.com/bug?extid=12b178b7c756664d2518
Tested-by: syzbot+12b178b7c756664d2518@xxxxxxxxxxxxxxxxxxxxxxxxx
Signed-off-by: Deepanshu Kartikey <kartikey406@xxxxxxxxx>
---
arch/arm64/kvm/reset.c | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c
index b963fd975aac..4ee16b4a37b5 100644
--- a/arch/arm64/kvm/reset.c
+++ b/arch/arm64/kvm/reset.c
@@ -25,6 +25,7 @@
#include <asm/ptrace.h>
#include <asm/kvm_arm.h>
#include <asm/kvm_asm.h>
+#include "vgic/vgic.h"
#include <asm/kvm_emulate.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_nested.h>
@@ -198,6 +199,14 @@ void kvm_reset_vcpu(struct kvm_vcpu *vcpu)
vcpu->arch.reset_state.reset = false;
spin_unlock(&vcpu->arch.mp_state_lock);
+
+ /*
+ * Initialize vGIC before entering preempt-disabled section.
+ * vgic_lazy_init() may sleep via mutex_lock, which is illegal
+ * inside preempt_disable(). Second call inside kvm_vgic_inject_irq
+ * will find vGIC already initialized and return immediately.
+ */
+ vgic_lazy_init(vcpu->kvm);
preempt_disable();
loaded = (vcpu->cpu != -1);
if (loaded)
--
2.43.0