[PATCH RFC] KVM: x86: Drop WARN_ON_ONCE() for concurrent PIC interrupt deassertion

"syzbot" <[email protected]>
Newsgroups dev.linux.lists.syzbot
Message-ID <[email protected]>
A false positive warning can be triggered in kvm_check_and_inject_events()
and vmx_check_nested_events() due to a natural race condition when a
level-triggered PIC interrupt is deasserted concurrently with vCPU entry.

The WARN_ON_ONCE(irq == -1) in kvm_check_and_inject_events() was added to
catch cases where kvm_cpu_has_injectable_intr() returns true, but
kvm_cpu_get_interrupt() returns -1 (meaning no interrupt is pending). The
assumption was that this could only happen due to a KVM bug. However, for
level-triggered PIC interrupts, a concurrent deassertion by the device
makes this a perfectly valid and expected scenario.

For example, if a device asserts a level-triggered interrupt, the IRR bit
is set and kvm_cpu_has_injectable_intr() returns true. If the device then
deasserts the interrupt before kvm_cpu_get_interrupt() is called, the IRR
bit is cleared, and kvm_cpu_get_interrupt() correctly returns -1.

A similar issue exists in vmx_check_nested_events(), where
kvm_cpu_has_interrupt() is checked first. If it returns true because a PIC
interrupt is pending, but the interrupt is concurrently deasserted, both
kvm_cpu_get_extint() and kvm_apic_has_interrupt() will return -1,
triggering a WARN_ON_ONCE(irq < 0).

Remove the WARN_ON_ONCE() macros in both functions, as WARN_ON must not be
used for conditions that can legitimately happen, and pr_err() should be
used instead if necessary. In this case, no error logging is needed because
the code already handles irq == -1 gracefully by skipping the injection,
which is the correct behavior when a level-triggered interrupt is
deasserted before it can be injected.

------------[ cut here ]------------
irq == -1
WARNING: arch/x86/kvm/x86.c:10860 at kvm_check_and_inject_events
arch/x86/kvm/x86.c:10860 [inline]
WARNING: arch/x86/kvm/x86.c:10860 at vcpu_enter_guest
arch/x86/kvm/x86.c:11356 [inline]
WARNING: arch/x86/kvm/x86.c:10860 at vcpu_run+0x57ec/0x7950
arch/x86/kvm/x86.c:11770
RIP: 0010:kvm_check_and_inject_events arch/x86/kvm/x86.c:10860 [inline]
RIP: 0010:vcpu_enter_guest arch/x86/kvm/x86.c:11356 [inline]
RIP: 0010:vcpu_run+0x57ec/0x7950 arch/x86/kvm/x86.c:11770
Call Trace:
 <TASK>
 kvm_arch_vcpu_ioctl_run+0x1193/0x2070 arch/x86/kvm/x86.c:12125
 kvm_vcpu_ioctl+0xa61/0xfd0 virt/kvm/kvm_main.c:4470
 vfs_ioctl fs/ioctl.c:51 [inline]
 __do_sys_ioctl fs/ioctl.c:597 [inline]
 __se_sys_ioctl+0xfc/0x170 fs/ioctl.c:583
 do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
 do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
 </TASK>

Fixes: bf672720e83c ("KVM: x86: check the kvm_cpu_get_interrupt result before using it")
Assisted-by: Gemini:gemini-3.1-pro-preview syzbot
Reported-by: [email protected]
Closes: https://syzkaller.appspot.com/bug?extid=dd769db18693736eee89
Link: https://syzkaller.appspot.com/ai_job?id=8568ceee-8527-4f42-b840-1fd49498df2e
To: "Borislav Petkov" <[email protected]>
To: "Dave Hansen" <[email protected]>
To: <[email protected]>
To: "Ingo Molnar" <[email protected]>
To: "Paolo Bonzini" <[email protected]>
To: "Sean Christopherson" <[email protected]>
To: "Thomas Gleixner" <[email protected]>
To: <[email protected]>
Cc: "H. Peter Anvin" <[email protected]>
Cc: <[email protected]>

---
diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c
index 3fe88f29b..72d434f3f 100644
--- a/arch/x86/kvm/vmx/nested.c
+++ b/arch/x86/kvm/vmx/nested.c
@@ -4446,7 +4446,12 @@ static int vmx_check_nested_events(struct kvm_vcpu *vcpu)
 		}
 
 		irq = kvm_apic_has_interrupt(vcpu);
-		if (WARN_ON_ONCE(irq < 0))
+		/*
+		 * A level-triggered PIC interrupt can be deasserted concurrently,
+		 * making it possible for irq to be -1 even if kvm_cpu_has_interrupt()
+		 * returned true.
+		 */
+		if (irq < 0)
 			goto no_vmexit;
 
 		/*
diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index 0550359ed..af36643b9 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -10857,7 +10857,13 @@ static int kvm_check_and_inject_events(struct kvm_vcpu *vcpu,
 		if (r) {
 			int irq = kvm_cpu_get_interrupt(vcpu);
 
-			if (!WARN_ON_ONCE(irq == -1)) {
+			/*
+			 * A level-triggered PIC interrupt can be deasserted
+			 * concurrently with vCPU entry, making it possible
+			 * for irq to be -1 even if kvm_cpu_has_injectable_intr()
+			 * returned true.
+			 */
+			if (irq != -1) {
 				kvm_queue_interrupt(vcpu, irq, false);
 				kvm_x86_call(inject_irq)(vcpu, false);
 				WARN_ON(kvm_x86_call(interrupt_allowed)(vcpu, true) < 0);


base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
-- 
This is an AI-generated patch subject to moderation.
Reply with '#syz upstream' to Sign-off the patch as a human author
and send it to the upstream kernel mailing lists.
Reply with '#syz reject' to reject it ('#syz unreject' to undo).

See https://goo.gle/syzbot-ai-patches for information about AI-generated patches.
You can comment on the patch as usual, syzbot will try to address
the comments and send a new version of the patch if necessary.
syzbot engineers can be reached at [email protected].
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.