Kernel KVM virtualization development
 help / color / mirror / Atom feed
* [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
       [not found] <20260705045450.1325048-2-bestswngs@gmail.com>
@ 2026-07-06 18:00 ` Weiming Shi
  2026-07-06 18:20   ` Sean Christopherson
                     ` (2 more replies)
  0 siblings, 3 replies; 7+ messages in thread
From: Weiming Shi @ 2026-07-06 18:00 UTC (permalink / raw)
  To: Sean Christopherson, Paolo Bonzini
  Cc: Kai Huang, kvm, stable, Zhang Haoyu, Jason Wang, Zhong Wang,
	Xuanqing Shi, Weiming Shi

kvm_ioapic_eoi_inject_work() re-delivers a throttled level-triggered
interrupt via kvm_irq_delivery_to_apic(), which walks kvm->arch.apic_map
and dereferences the destination vCPU's APIC.  The work is cancelled only
in kvm_ioapic_destroy(), which runs after kvm_destroy_vcpus() has freed
the vCPUs and their APICs.  kvm_free_lapic() does not rebuild apic_map, so
the map is left with dangling pointers, and a work item that fires during
that window reads freed memory:

 BUG: KASAN: slab-use-after-free in __kvm_irq_delivery_to_apic_fast (arch/x86/kvm/lapic.c:1248)
 Read of size 8 by task kworker/3:1
 Workqueue: events kvm_ioapic_eoi_inject_work
  __kvm_irq_delivery_to_apic_fast (arch/x86/kvm/lapic.c:1248)
  __kvm_irq_delivery_to_apic (arch/x86/kvm/lapic.c:1343)
  ioapic_service (arch/x86/kvm/ioapic.c:492)
  kvm_ioapic_eoi_inject_work (arch/x86/kvm/ioapic.c:525)
  process_one_work

 Freed by task 153:
  kvm_arch_vcpu_destroy (arch/x86/kvm/x86.c:12871)
  kvm_destroy_vcpus (virt/kvm/kvm_main.c:489)
  kvm_arch_destroy_vm (arch/x86/kvm/x86.c:13402)
  kvm_destroy_vm (virt/kvm/kvm_main.c:1302)
  kvm_vm_release (virt/kvm/kvm_main.c:1363)

A guest arms the work by EOIing a level-triggered pin 10000 times in a
row, so the window is reachable from guest ring 0 whenever its VM is torn
down soon after.

Destroy the in-kernel PIC and IOAPIC in kvm_arch_pre_destroy_vm(),
before vCPUs are freed, so the eoi_inject work is cancelled while the
target APICs are still valid.  This also unregisters the PIC/IOAPIC
MMIO devices while the KVM buses still exist; kvm_destroy_vm() tears
the buses down right after kvm_free_irq_routing() and before
kvm_arch_destroy_vm(), so the previous kvm_io_bus_unregister_dev() in
kvm_ioapic_destroy() was a no-op.

Fixes: 184564efae4d ("kvm: ioapic: conditionally delay irq delivery duringeoi broadcast")
Link: https://lore.kernel.org/all/88ba60ad32ba851426a3f6590b0e402210991b4a.e33b58ce.0008.4e1c.aa62.c1024b242cbf@bytedance.com/
Suggested-by: Kai Huang <kai.huang@intel.com>
Reported-by: Zhong Wang <wangzhong.c0ss4ck@bytedance.com>
Reported-by: Xuanqing Shi <shixuanqing.11@bytedance.com>
Signed-off-by: Weiming Shi <bestswngs@gmail.com>
---
v1: https://lore.kernel.org/all/20260705045450.1325048-2-bestswngs@gmail.com/

v2:
 - Per Kai's suggestion, instead of adding a kvm_ioapic_pre_destroy()
   helper that only cancels the eoi_inject work, move
   kvm_pic_destroy()/kvm_ioapic_destroy() as a whole into
   kvm_arch_pre_destroy_vm().  This also fixes the stale
   kvm_io_bus_unregister_dev() Kai pointed out.
 arch/x86/kvm/x86.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
index fd1c4a36b593..5925da351a9a 100644
--- a/arch/x86/kvm/x86.c
+++ b/arch/x86/kvm/x86.c
@@ -13370,10 +13370,14 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm)
 	 * Stop all background workers and kthreads before destroying vCPUs, as
 	 * iterating over vCPUs in a different task while vCPUs are being freed
 	 * is unsafe, i.e. will lead to use-after-free.  The PIT also needs to
-	 * be stopped before IRQ routing is freed.
+	 * be stopped before IRQ routing is freed.  The PIC and IOAPIC need to
+	 * be destroyed here too, for the same reason, and because they must
+	 * be destroyed before the KVM buses are torn down.
 	 */
 #ifdef CONFIG_KVM_IOAPIC
 	kvm_free_pit(kvm);
+	kvm_pic_destroy(kvm);
+	kvm_ioapic_destroy(kvm);
 #endif
 
 	kvm_mmu_pre_destroy_vm(kvm);
@@ -13400,10 +13404,6 @@ void kvm_arch_destroy_vm(struct kvm *kvm)
 		perf_release_mediated_pmu();
 	kvm_destroy_vcpus(kvm);
 	kvm_free_msr_filter(srcu_dereference_check(kvm->arch.msr_filter, &kvm->srcu, 1));
-#ifdef CONFIG_KVM_IOAPIC
-	kvm_pic_destroy(kvm);
-	kvm_ioapic_destroy(kvm);
-#endif
 	kvfree(rcu_dereference_check(kvm->arch.apic_map, 1));
 	kfree(srcu_dereference_check(kvm->arch.pmu_event_filter, &kvm->srcu, 1));
 	kvm_mmu_uninit_vm(kvm);
-- 
2.54.0


^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
  2026-07-06 18:00 ` [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs Weiming Shi
@ 2026-07-06 18:20   ` Sean Christopherson
  2026-07-06 22:06     ` Huang, Kai
  2026-07-06 18:20   ` sashiko-bot
  2026-07-07  6:32   ` [syzbot ci] " syzbot ci
  2 siblings, 1 reply; 7+ messages in thread
From: Sean Christopherson @ 2026-07-06 18:20 UTC (permalink / raw)
  To: Weiming Shi
  Cc: Paolo Bonzini, Kai Huang, kvm, stable, Zhang Haoyu, Jason Wang,
	Zhong Wang, Xuanqing Shi

On Tue, Jul 07, 2026, Weiming Shi wrote:
> kvm_ioapic_eoi_inject_work() re-delivers a throttled level-triggered
> interrupt via kvm_irq_delivery_to_apic(), which walks kvm->arch.apic_map
> and dereferences the destination vCPU's APIC.  The work is cancelled only
> in kvm_ioapic_destroy(), which runs after kvm_destroy_vcpus() has freed
> the vCPUs and their APICs.  kvm_free_lapic() does not rebuild apic_map, so
> the map is left with dangling pointers, and a work item that fires during
> that window reads freed memory:
> 
>  BUG: KASAN: slab-use-after-free in __kvm_irq_delivery_to_apic_fast (arch/x86/kvm/lapic.c:1248)
>  Read of size 8 by task kworker/3:1
>  Workqueue: events kvm_ioapic_eoi_inject_work
>   __kvm_irq_delivery_to_apic_fast (arch/x86/kvm/lapic.c:1248)
>   __kvm_irq_delivery_to_apic (arch/x86/kvm/lapic.c:1343)
>   ioapic_service (arch/x86/kvm/ioapic.c:492)
>   kvm_ioapic_eoi_inject_work (arch/x86/kvm/ioapic.c:525)
>   process_one_work
> 
>  Freed by task 153:
>   kvm_arch_vcpu_destroy (arch/x86/kvm/x86.c:12871)
>   kvm_destroy_vcpus (virt/kvm/kvm_main.c:489)
>   kvm_arch_destroy_vm (arch/x86/kvm/x86.c:13402)
>   kvm_destroy_vm (virt/kvm/kvm_main.c:1302)
>   kvm_vm_release (virt/kvm/kvm_main.c:1363)
> 
> A guest arms the work by EOIing a level-triggered pin 10000 times in a
> row, so the window is reachable from guest ring 0 whenever its VM is torn
> down soon after.
> 
> Destroy the in-kernel PIC and IOAPIC in kvm_arch_pre_destroy_vm(),
> before vCPUs are freed, so the eoi_inject work is cancelled while the
> target APICs are still valid.  This also unregisters the PIC/IOAPIC
> MMIO devices while the KVM buses still exist; kvm_destroy_vm() tears
> the buses down right after kvm_free_irq_routing() and before
> kvm_arch_destroy_vm(), so the previous kvm_io_bus_unregister_dev() in
> kvm_ioapic_destroy() was a no-op.
> 
> Fixes: 184564efae4d ("kvm: ioapic: conditionally delay irq delivery duringeoi broadcast")
> Link: https://lore.kernel.org/all/88ba60ad32ba851426a3f6590b0e402210991b4a.e33b58ce.0008.4e1c.aa62.c1024b242cbf@bytedance.com/
> Suggested-by: Kai Huang <kai.huang@intel.com>
> Reported-by: Zhong Wang <wangzhong.c0ss4ck@bytedance.com>
> Reported-by: Xuanqing Shi <shixuanqing.11@bytedance.com>
> Signed-off-by: Weiming Shi <bestswngs@gmail.com>
> ---
> v1: https://lore.kernel.org/all/20260705045450.1325048-2-bestswngs@gmail.com/
> 
> v2:
>  - Per Kai's suggestion, instead of adding a kvm_ioapic_pre_destroy()
>    helper that only cancels the eoi_inject work, move
>    kvm_pic_destroy()/kvm_ioapic_destroy() as a whole into
>    kvm_arch_pre_destroy_vm().  This also fixes the stale
>    kvm_io_bus_unregister_dev() Kai pointed out.

Sadly, it creates an even easier-to-exploit NULL pointer deref.  Your v1 is what
I came up with idependently (the bug got reported off-list), though I eschewed a
helper.

Author:     Sean Christopherson <seanjc@google.com>
AuthorDate: Tue Jun 30 08:13:42 2026 -0700
Commit:     Sean Christopherson <seanjc@google.com>
CommitDate: Tue Jun 30 08:28:19 2026 -0700

    KVM: x86: Cancel delayed I/O APIC EOI handling before destroying vCPUs
    
    Cancel (and flush) the I/O APIC's delayed EOI handling work during the
    "pre VM destroy" phase, before vCPUs are destroyed, as processing the EOI
    broadcast will inject another IRQ if the line is asserted, i.e. will try
    to deliver an IRQ to the target vCPU(s).  Canceling the work after vCPUs
    are destroyed leads to UAF if the delayed work is processed after vCPUs are
    destroyed.
    
      BUG: KASAN: slab-use-after-free in __kvm_irq_delivery_to_apic_fast+0x9bf/0xa20 arch/x86/kvm/lapic.c:1250
      Read of size 8 at addr ffff8880499abea0 by task kworker/1:2/1218
    
      CPU: 1 UID: 0 PID: 1218 Comm: kworker/1:2 Not tainted 7.1.0-rc7 #5 PREEMPT(lazy)
      Hardware name: QEMU Ubuntu 25.10 PC v2 (i440FX + PIIX, + 10.1 machine, 1996), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
      Workqueue: events kvm_ioapic_eoi_inject_work
      Call Trace:
       <TASK>
       __dump_stack /root/linux/lib/dump_stack.c:94
       dump_stack_lvl+0x100/0x190 /root/linux/lib/dump_stack.c:120
       print_address_description /root/linux/mm/kasan/report.c:378
       print_report+0x139/0x4ad /root/linux/mm/kasan/report.c:482
       kasan_report+0xe4/0x1d0 /root/linux/mm/kasan/report.c:595
       __kvm_irq_delivery_to_apic_fast+0x9bf/0xa20 /root/linux/arch/x86/kvm/lapic.c:1250
       __kvm_irq_delivery_to_apic+0xd8/0xbf0 /root/linux/arch/x86/kvm/lapic.c:1345
       kvm_irq_delivery_to_apic /root/linux/arch/x86/kvm/lapic.h:129
       ioapic_service+0x308/0x590 /root/linux/arch/x86/kvm/ioapic.c:492
       kvm_ioapic_eoi_inject_work+0x13c/0x190 /root/linux/arch/x86/kvm/ioapic.c:532
       process_one_work+0xa59/0x19a0 /root/linux/kernel/workqueue.c:3314
       process_scheduled_works /root/linux/kernel/workqueue.c:3397
       worker_thread+0x5eb/0xe50 /root/linux/kernel/workqueue.c:3478
       kthread+0x370/0x450 /root/linux/kernel/kthread.c:436
       ret_from_fork+0x72b/0xd30 /root/linux/arch/x86/kernel/process.c:158
       ret_from_fork_asm+0x1a/0x30 /root/linux/arch/x86/entry/entry_64.S:245
       </TASK>
    
    Note, the VM is unreachable once kvm_destroy_vm() starts, and scheduling
    new work via kvm_ioapic_send_eoi() can only be done via KVM_RUN, i.e.
    requires a live vCPU.
    
    Alternatively, KVM could simply destroy the I/O APIC during the "pre" phase
    of VM destruction, but that gets more than a bit sketchy as KVM expects the
    I/O APIC to exist if ioapic_in_kernel() is true, and nested virtualization
    in particular has a bad habit of touching VM-scope state during vCPU
    destruction.  E.g. attempting to free the PIC during the pre phase would
    lead to a NULL pointer dereference in kvm_cpu_has_extint(), and it's not
    hard to imagine the I/O APIC having a similar flaw.
    
    Fixes: 17bcd7144263 ("KVM: x86: Free vCPUs before freeing VM state")
    Reported-by: <zdi-disclosures@trendmicro.com>
    Cc: stable@vger.kernel.org
    Signed-off-by: Sean Christopherson <seanjc@google.com>

diff --git arch/x86/kvm/x86.c arch/x86/kvm/x86.c
index 0626e835e9eb..a0cc74c8ded1 100644
--- arch/x86/kvm/x86.c
+++ arch/x86/kvm/x86.c
@@ -9942,6 +9942,8 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm)
         */
 #ifdef CONFIG_KVM_IOAPIC
        kvm_free_pit(kvm);
+       if (kvm->arch.vioapic)
+               cancel_delayed_work_sync(&kvm->arch.vioapic->eoi_inject);
 #endif
 
        kvm_mmu_pre_destroy_vm(kvm);

^ permalink raw reply related	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
  2026-07-06 18:00 ` [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs Weiming Shi
  2026-07-06 18:20   ` Sean Christopherson
@ 2026-07-06 18:20   ` sashiko-bot
  2026-07-07  6:32   ` [syzbot ci] " syzbot ci
  2 siblings, 0 replies; 7+ messages in thread
From: sashiko-bot @ 2026-07-06 18:20 UTC (permalink / raw)
  To: Weiming Shi; +Cc: kvm

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] Use-after-free on device error path in kvm_create_pit().
--

commit 738d33431d0b6932537e95c21eed6b24c1201219
Author: Weiming Shi <bestswngs@gmail.com>

KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs

This commit moves the destruction of the in-kernel PIC and IOAPIC to
kvm_arch_pre_destroy_vm(), before vCPUs are freed. This ensures that the
eoi_inject work is cancelled while target APICs remain valid. It prevents
a use-after-free where a delayed work item could dereference a freed vCPU's
APIC map.

Link: https://lore.kernel.org/all/88ba60ad32ba851426a3f6590b0e402210991b4a.e33b58ce.0008.4e1c.aa62.c1024b242cbf@bytedance.com/

> diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c
> index afcac1042947a..38ac0e0a3f769 100644
> --- a/arch/x86/kvm/x86.c
> +++ b/arch/x86/kvm/x86.c
> @@ -13428,10 +13428,14 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm)
>  	 * Stop all background workers and kthreads before destroying vCPUs, as
>  	 * iterating over vCPUs in a different task while vCPUs are being freed
>  	 * is unsafe, i.e. will lead to use-after-free.  The PIT also needs to
> -	 * be stopped before IRQ routing is freed.
> +	 * be stopped before IRQ routing is freed.  The PIC and IOAPIC need to
> +	 * be destroyed here too, for the same reason, and because they must
> +	 * be destroyed before the KVM buses are torn down.
>  	 */
>  #ifdef CONFIG_KVM_IOAPIC
>  	kvm_free_pit(kvm);
[Severity: High]
This isn't a bug introduced by this patch, but since we are touching the PIT
and IOAPIC destruction paths here, is there a potential use-after-free on the
device error path in kvm_create_pit()?

If host userspace creates a VM, initializes the IRQCHIP, creates a vCPU,
and starts running it, the vCPU can perform PIO exits, calling
kvm_io_bus_read() and acquiring srcu_read_lock(&kvm->srcu).

If userspace concurrently calls KVM_CREATE_PIT2 with the
KVM_PIT_SPEAKER_DUMMY flag, the kernel registers pit->dev on the PIO bus.
The vCPU thread could pick this up.

If the subsequent registration of pit->speaker_dev fails, the error path
unregisters pit->dev but frees it without waiting for SRCU readers:

arch/x86/kvm/i8254.c:kvm_create_pit() {
    ...
fail_register_speaker:
	kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &pit->dev);
fail_register_pit:
	mutex_unlock(&kvm->slots_lock);
	kvm_pit_set_reinject(pit, false);
	kthread_destroy_worker(pit->worker);
fail_kthread:
	kfree(pit);
	return NULL;
}

Could the vCPU thread, while iterating over the old bus, dereference
pit->dev->ops and access the freed memory?

> +	kvm_pic_destroy(kvm);
> +	kvm_ioapic_destroy(kvm);
>  #endif

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260706180025.2735341-3-bestswngs@gmail.com?part=1

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
  2026-07-06 18:20   ` Sean Christopherson
@ 2026-07-06 22:06     ` Huang, Kai
  2026-07-06 22:10       ` Sean Christopherson
  0 siblings, 1 reply; 7+ messages in thread
From: Huang, Kai @ 2026-07-06 22:06 UTC (permalink / raw)
  To: seanjc@google.com, bestswngs@gmail.com
  Cc: jasowang@redhat.com, kvm@vger.kernel.org, pbonzini@redhat.com,
	stable@vger.kernel.org, zhanghy@sangfor.com, Wang, Zhong,
	shixuanqing.11@bytedance.com


>     Alternatively, KVM could simply destroy the I/O APIC during the "pre" phase
>     of VM destruction, but that gets more than a bit sketchy as KVM expects the
>     I/O APIC to exist if ioapic_in_kernel() is true, and nested virtualization
>     in particular has a bad habit of touching VM-scope state during vCPU
>     destruction.  E.g. attempting to free the PIC during the pre phase would
>     lead to a NULL pointer dereference in kvm_cpu_has_extint(), and it's not
>     hard to imagine the I/O APIC having a similar flaw.

Hmm seems vmx_vcpu_free() can eventually call into kvm_cpu_has_extint() via
nested_vmx_vmexit().  Thanks for pointing out.

>     
>     Fixes: 17bcd7144263 ("KVM: x86: Free vCPUs before freeing VM state")
>     Reported-by: <zdi-disclosures@trendmicro.com>
>     Cc: stable@vger.kernel.org
>     Signed-off-by: Sean Christopherson <seanjc@google.com>
> 
> diff --git arch/x86/kvm/x86.c arch/x86/kvm/x86.c
> index 0626e835e9eb..a0cc74c8ded1 100644
> --- arch/x86/kvm/x86.c
> +++ arch/x86/kvm/x86.c
> @@ -9942,6 +9942,8 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm)
>          */
>  #ifdef CONFIG_KVM_IOAPIC
>         kvm_free_pit(kvm);
> +       if (kvm->arch.vioapic)
> +               cancel_delayed_work_sync(&kvm->arch.vioapic->eoi_inject);

Maybe add a comment to explain why we cannot destroy IOAPIC and PIC here?

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
  2026-07-06 22:06     ` Huang, Kai
@ 2026-07-06 22:10       ` Sean Christopherson
  2026-07-06 22:33         ` Huang, Kai
  0 siblings, 1 reply; 7+ messages in thread
From: Sean Christopherson @ 2026-07-06 22:10 UTC (permalink / raw)
  To: Kai Huang
  Cc: bestswngs@gmail.com, jasowang@redhat.com, kvm@vger.kernel.org,
	pbonzini@redhat.com, stable@vger.kernel.org, zhanghy@sangfor.com,
	Zhong Wang, shixuanqing.11@bytedance.com

On Mon, Jul 06, 2026, Kai Huang wrote:
> 
> >     Alternatively, KVM could simply destroy the I/O APIC during the "pre" phase
> >     of VM destruction, but that gets more than a bit sketchy as KVM expects the
> >     I/O APIC to exist if ioapic_in_kernel() is true, and nested virtualization
> >     in particular has a bad habit of touching VM-scope state during vCPU
> >     destruction.  E.g. attempting to free the PIC during the pre phase would
> >     lead to a NULL pointer dereference in kvm_cpu_has_extint(), and it's not
> >     hard to imagine the I/O APIC having a similar flaw.
> 
> Hmm seems vmx_vcpu_free() can eventually call into kvm_cpu_has_extint() via
> nested_vmx_vmexit().  Thanks for pointing out.

Yeah, I found out the hard way :-)

> >     Fixes: 17bcd7144263 ("KVM: x86: Free vCPUs before freeing VM state")
> >     Reported-by: <zdi-disclosures@trendmicro.com>
> >     Cc: stable@vger.kernel.org
> >     Signed-off-by: Sean Christopherson <seanjc@google.com>
> > 
> > diff --git arch/x86/kvm/x86.c arch/x86/kvm/x86.c
> > index 0626e835e9eb..a0cc74c8ded1 100644
> > --- arch/x86/kvm/x86.c
> > +++ arch/x86/kvm/x86.c
> > @@ -9942,6 +9942,8 @@ void kvm_arch_pre_destroy_vm(struct kvm *kvm)
> >          */
> >  #ifdef CONFIG_KVM_IOAPIC
> >         kvm_free_pit(kvm);
> > +       if (kvm->arch.vioapic)
> > +               cancel_delayed_work_sync(&kvm->arch.vioapic->eoi_inject);
> 
> Maybe add a comment to explain why we cannot destroy IOAPIC and PIC here?

Hmm, agreed, the block comment above can/should be extended to explain why it's
ok to destroy the PIT, but not the PIC or I/O APIC.

^ permalink raw reply	[flat|nested] 7+ messages in thread

* Re: [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
  2026-07-06 22:10       ` Sean Christopherson
@ 2026-07-06 22:33         ` Huang, Kai
  0 siblings, 0 replies; 7+ messages in thread
From: Huang, Kai @ 2026-07-06 22:33 UTC (permalink / raw)
  To: seanjc@google.com
  Cc: pbonzini@redhat.com, Wang, Zhong, zhanghy@sangfor.com,
	shixuanqing.11@bytedance.com, bestswngs@gmail.com,
	kvm@vger.kernel.org, stable@vger.kernel.org, jasowang@redhat.com

On Mon, 2026-07-06 at 15:10 -0700, Sean Christopherson wrote:
> On Mon, Jul 06, 2026, Kai Huang wrote:
> > 
> > >     Alternatively, KVM could simply destroy the I/O APIC during the "pre" phase
> > >     of VM destruction, but that gets more than a bit sketchy as KVM expects the
> > >     I/O APIC to exist if ioapic_in_kernel() is true, and nested virtualization
> > >     in particular has a bad habit of touching VM-scope state during vCPU
> > >     destruction.  E.g. attempting to free the PIC during the pre phase would
> > >     lead to a NULL pointer dereference in kvm_cpu_has_extint(), and it's not
> > >     hard to imagine the I/O APIC having a similar flaw.
> > 
> > Hmm seems vmx_vcpu_free() can eventually call into kvm_cpu_has_extint() via
> > nested_vmx_vmexit().  Thanks for pointing out.
> 
> Yeah, I found out the hard way :-)

:-)

Btw, I don't think the order of kvm_free_irq_routine() and destroying IOAPIC/PIC
is a concern here?  As you mentioned, once kvm_destroy_vm() starts, VM is not
touchable from userspace, therefore it should not be possible to have code path
which can reference vIOAPIC/vPIC after they got destroyed but before
kvm_free_irq_routine() through IRQ routine table?

^ permalink raw reply	[flat|nested] 7+ messages in thread

* [syzbot ci] Re: KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
  2026-07-06 18:00 ` [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs Weiming Shi
  2026-07-06 18:20   ` Sean Christopherson
  2026-07-06 18:20   ` sashiko-bot
@ 2026-07-07  6:32   ` syzbot ci
  2 siblings, 0 replies; 7+ messages in thread
From: syzbot ci @ 2026-07-07  6:32 UTC (permalink / raw)
  To: bestswngs, jasowang, kai.huang, kvm, pbonzini, seanjc,
	shixuanqing.11, stable, wangzhong.c0ss4ck, zhanghy
  Cc: syzbot, syzkaller-bugs

syzbot ci has tested the following series

[v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs
https://lore.kernel.org/all/20260706180025.2735341-3-bestswngs@gmail.com
* [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs

and found the following issue:
general protection fault in kvm_cpu_has_extint

Full report is available here:
https://ci.syzbot.org/series/43a61d29-79d6-47db-ac80-4e948bd10c1a

***

general protection fault in kvm_cpu_has_extint

tree:      kvm-next
URL:       https://kernel.googlesource.com/pub/scm/virt/kvm/kvm/
base:      fb402386af4cdce108ff991a796386de55439735
arch:      amd64
compiler:  Debian clang version 22.1.6 (++20260514074242+fc4aad7b5db3-1~exp1~20260514074407.73), Debian LLD 22.1.6
config:    https://ci.syzbot.org/builds/7f3721c2-14ce-4beb-aa97-08232f57e71a/config
syz repro: https://ci.syzbot.org/findings/0522c527-432c-4524-9835-64ab75b899af/syz_repro

Oops: general protection fault, probably for non-canonical address 0xdffffc0000000012: 0000 [#1] SMP KASAN NOPTI
KASAN: null-ptr-deref in range [0x0000000000000090-0x0000000000000097]
CPU: 1 UID: 0 PID: 5786 Comm: syz.2.19 Not tainted syzkaller #0 PREEMPT(full) 
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.2-debian-1.16.2-1 04/01/2014
RIP: 0010:kvm_cpu_has_extint+0xdb/0x340 arch/x86/kvm/irq.c:83
Code: c6 e8 0d 00 00 4c 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 44 54 e0 00 bb 90 00 00 00 49 03 1e 48 89 d8 48 c1 e8 03 <42> 0f b6 04 38 84 c0 0f 85 aa 00 00 00 8b 2b eb 6d e8 df 08 76 00
RSP: 0018:ffffc900032f7740 EFLAGS: 00010206
RAX: 0000000000000012 RBX: 0000000000000090 RCX: ffff88816b748000
RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000000000001
RBP: 0000000000000001 R08: ffff88816b748000 R09: 0000000000000006
R10: 0000000000000006 R11: 0000000000000000 R12: 1ffff1102e032000
R13: dffffc0000000000 R14: ffff88811e33cde8 R15: dffffc0000000000
FS:  00007f57357e76c0(0000) GS:ffff8882a9714000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 00007fae800f0378 CR3: 0000000109ffc000 CR4: 0000000000352ef0
Call Trace:
 <TASK>
 kvm_cpu_has_injectable_intr+0x1c/0x170 arch/x86/kvm/irq.c:98
 __nested_vmx_vmexit+0x1911/0x2b40 arch/x86/kvm/vmx/nested.c:5181
 nested_vmx_vmexit arch/x86/kvm/vmx/nested.h:46 [inline]
 vmx_leave_nested arch/x86/kvm/vmx/nested.c:6854 [inline]
 nested_vmx_free_vcpu+0x8e/0xd0 arch/x86/kvm/vmx/nested.c:381
 vmx_vcpu_free+0x109/0x2c0 arch/x86/kvm/vmx/vmx.c:7765
 kvm_arch_vcpu_destroy+0x154/0x380 arch/x86/kvm/x86.c:12859
 kvm_vcpu_destroy virt/kvm/kvm_main.c:469 [inline]
 kvm_destroy_vcpus+0x123/0x380 virt/kvm/kvm_main.c:489
 kvm_arch_destroy_vm+0xf9/0x2c0 arch/x86/kvm/x86.c:13405
 kvm_destroy_vm virt/kvm/kvm_main.c:1301 [inline]
 kvm_put_kvm+0x772/0xb10 virt/kvm/kvm_main.c:1338
 kvm_vcpu_release+0x54/0x60 virt/kvm/kvm_main.c:4101
 __fput+0x41f/0xa40 fs/file_table.c:469
 task_work_run+0x1d9/0x270 kernel/task_work.c:233
 get_signal+0x1181/0x12c0 kernel/signal.c:2807
 arch_do_signal_or_restart+0xbb/0x810 arch/x86/kernel/signal.c:337
 __exit_to_user_mode_loop kernel/entry/common.c:64 [inline]
 exit_to_user_mode_loop+0xa3/0x5e0 kernel/entry/common.c:98
 __exit_to_user_mode_prepare include/linux/irq-entry-common.h:226 [inline]
 syscall_exit_to_user_mode_prepare include/linux/irq-entry-common.h:256 [inline]
 syscall_exit_to_user_mode include/linux/entry-common.h:325 [inline]
 do_syscall_64+0x357/0xf80 arch/x86/entry/syscall_64.c:100
 entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f573499ce59
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f57357e7028 EFLAGS: 00000246 ORIG_RAX: 0000000000000010
RAX: fffffffffffffffc RBX: 00007f5734c15fa0 RCX: 00007f573499ce59
RDX: 0000000000000000 RSI: 000000000000ae80 RDI: 0000000000000006
RBP: 00007f5734a32e6f R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f5734c16038 R14: 00007f5734c15fa0 R15: 00007fffda4d80f8
 </TASK>
Modules linked in:
---[ end trace 0000000000000000 ]---
RIP: 0010:kvm_cpu_has_extint+0xdb/0x340 arch/x86/kvm/irq.c:83
Code: c6 e8 0d 00 00 4c 89 f0 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 f7 e8 44 54 e0 00 bb 90 00 00 00 49 03 1e 48 89 d8 48 c1 e8 03 <42> 0f b6 04 38 84 c0 0f 85 aa 00 00 00 8b 2b eb 6d e8 df 08 76 00
RSP: 0018:ffffc900032f7740 EFLAGS: 00010206
RAX: 0000000000000012 RBX: 0000000000000090 RCX: ffff88816b748000
RDX: 0000000000000000 RSI: 0000000000000001 RDI: 0000000000000001
RBP: 0000000000000001 R08: ffff88816b748000 R09: 0000000000000006
R10: 0000000000000006 R11: 0000000000000000 R12: 1ffff1102e032000
R13: dffffc0000000000 R14: ffff88811e33cde8 R15: dffffc0000000000
FS:  00007f57357e76c0(0000) GS:ffff8882a9714000(0000) knlGS:0000000000000000
CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000056532c5e2950 CR3: 0000000109ffc000 CR4: 0000000000352ef0
----------------
Code disassembly (best guess), 1 bytes skipped:
   0:	e8 0d 00 00 4c       	call   0x4c000012
   5:	89 f0                	mov    %esi,%eax
   7:	48 c1 e8 03          	shr    $0x3,%rax
   b:	42 80 3c 38 00       	cmpb   $0x0,(%rax,%r15,1)
  10:	74 08                	je     0x1a
  12:	4c 89 f7             	mov    %r14,%rdi
  15:	e8 44 54 e0 00       	call   0xe0545e
  1a:	bb 90 00 00 00       	mov    $0x90,%ebx
  1f:	49 03 1e             	add    (%r14),%rbx
  22:	48 89 d8             	mov    %rbx,%rax
  25:	48 c1 e8 03          	shr    $0x3,%rax
* 29:	42 0f b6 04 38       	movzbl (%rax,%r15,1),%eax <-- trapping instruction
  2e:	84 c0                	test   %al,%al
  30:	0f 85 aa 00 00 00    	jne    0xe0
  36:	8b 2b                	mov    (%rbx),%ebp
  38:	eb 6d                	jmp    0xa7
  3a:	e8 df 08 76 00       	call   0x76091e


***

If these findings have caused you to resend the series or submit a
separate fix, please add the following tag to your commit message:
  Tested-by: syzbot@syzkaller.appspotmail.com

---
This report is generated by a bot. It may contain errors.
syzbot ci engineers can be reached at syzkaller@googlegroups.com.

To test a patch for this bug, please reply with `#syz test`
(should be on a separate line).

The patch should be attached to the email.
Note: arguments like custom git repos and branches are not supported.

^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-07-07  6:32 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <20260705045450.1325048-2-bestswngs@gmail.com>
2026-07-06 18:00 ` [PATCH v2] KVM: x86: Destroy the PIC and IOAPIC before destroying vCPUs Weiming Shi
2026-07-06 18:20   ` Sean Christopherson
2026-07-06 22:06     ` Huang, Kai
2026-07-06 22:10       ` Sean Christopherson
2026-07-06 22:33         ` Huang, Kai
2026-07-06 18:20   ` sashiko-bot
2026-07-07  6:32   ` [syzbot ci] " syzbot ci

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox