--- /dev/null
+From 2abd5287f08319fa35764566b15c6e22cb1068db Mon Sep 17 00:00:00 2001
+From: Sean Christopherson <seanjc@google.com>
+Date: Mon, 13 Jul 2026 08:15:33 -0700
+Subject: KVM: x86: Check for invalid/obsolete root *after* making MMU pages available
+
+From: Sean Christopherson <seanjc@google.com>
+
+commit 2abd5287f08319fa35764566b15c6e22cb1068db upstream.
+
+Check for a "stale" page fault, i.e. for an invalid and/or obsolete root,
+after making MMU pages available for the shadow MMU. If reclaiming shadow
+pages zaps an in-use root, i.e. marks it invalid, then KVM will attempt to
+map memory into an invalid root. On its own, populating an invalid root is
+"fine", but because child shadow pages inherit their parent's role, any
+children created during the map/fetch will be created as invalid pages,
+thus violating KVM's invariant that invalid pages are never on the list of
+active MMU pages.
+
+Note, the underlying flaw has existed since KVM first started tracking
+invalid roots in 2008 (commit 2e53d63acba7, "KVM: MMU: ignore zapped root
+pagetables"), but the true badness only came along in 2020 (Linux 5.9)
+with the invariant that invalid shadow pages can't be on the list of
+active pages.
+
+Note #2, inheriting role.invalid when creating child shadow pages is also
+far from ideal; that flaw will be addressed separately.
+
+Reported-by: Hyunwoo Kim <imv4bel@gmail.com>
+Fixes: f95eec9bed76 ("KVM: x86/mmu: Don't put invalid SPs back on the list of active pages")
+Cc: stable@vger.kernel.org
+Signed-off-by: Sean Christopherson <seanjc@google.com>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/kvm/mmu/mmu.c | 9 +++++----
+ arch/x86/kvm/mmu/paging_tmpl.h | 10 ++++++----
+ 2 files changed, 11 insertions(+), 8 deletions(-)
+
+--- a/arch/x86/kvm/mmu/mmu.c
++++ b/arch/x86/kvm/mmu/mmu.c
+@@ -4837,16 +4837,17 @@ static int direct_page_fault(struct kvm_
+ if (r != RET_PF_CONTINUE)
+ return r;
+
+- r = RET_PF_RETRY;
+ write_lock(&vcpu->kvm->mmu_lock);
+
+- if (is_page_fault_stale(vcpu, fault))
+- goto out_unlock;
+-
+ r = make_mmu_pages_available(vcpu);
+ if (r)
+ goto out_unlock;
+
++ if (is_page_fault_stale(vcpu, fault)) {
++ r = RET_PF_RETRY;
++ goto out_unlock;
++ }
++
+ r = direct_map(vcpu, fault);
+
+ out_unlock:
+--- a/arch/x86/kvm/mmu/paging_tmpl.h
++++ b/arch/x86/kvm/mmu/paging_tmpl.h
+@@ -827,15 +827,17 @@ static int FNAME(page_fault)(struct kvm_
+ }
+ #endif
+
+- r = RET_PF_RETRY;
+ write_lock(&vcpu->kvm->mmu_lock);
+
+- if (is_page_fault_stale(vcpu, fault))
+- goto out_unlock;
+-
+ r = make_mmu_pages_available(vcpu);
+ if (r)
+ goto out_unlock;
++
++ if (is_page_fault_stale(vcpu, fault)) {
++ r = RET_PF_RETRY;
++ goto out_unlock;
++ }
++
+ r = FNAME(fetch)(vcpu, fault, &walker);
+
+ out_unlock:
--- /dev/null
+From 52f2f7c30126037975389aa04d24c506a5177c35 Mon Sep 17 00:00:00 2001
+From: Phil Rosenthal <phil@phil.gs>
+Date: Sat, 18 Jul 2026 12:50:23 -0400
+Subject: KVM: x86/mmu: Fix use-after-free on vendor module reload
+
+From: Phil Rosenthal <phil@phil.gs>
+
+commit 52f2f7c30126037975389aa04d24c506a5177c35 upstream.
+
+mmu_destroy_caches() destroys pte_list_desc_cache and
+mmu_page_header_cache, but leaves both pointers unchanged. The pointers
+live in kvm.ko, and therefore survive when a vendor module is unloaded
+while kvm.ko remains loaded.
+
+If creation of pte_list_desc_cache fails during a subsequent vendor
+module load, its assignment sets pte_list_desc_cache to NULL and the
+error path calls mmu_destroy_caches(). mmu_page_header_cache still
+points to the cache destroyed during the preceding vendor module
+unload. Passing that stale pointer to kmem_cache_destroy() causes a
+slab use-after-free.
+
+Reproduce the issue on a v7.1.3 kernel with CONFIG_KASAN=y,
+CONFIG_KASAN_GENERIC=y, CONFIG_KVM=m, and CONFIG_KVM_INTEL=m. A
+one-shot test hook forces pte_list_desc_cache to NULL on the second
+invocation of kvm_mmu_vendor_module_init():
+
+ 1. Load kvm.ko and kvm-intel.ko, creating both caches.
+ 2. Unload only kvm_intel, leaving kvm.ko loaded.
+ 3. Reload kvm_intel and force initialization through the -ENOMEM path.
+
+KASAN reports:
+
+ BUG: KASAN: slab-use-after-free in
+ kvm_mmu_vendor_module_init+0x5b/0x170 [kvm]
+ ...
+ kmem_cache_destroy+0x21/0x1d0
+ kvm_mmu_vendor_module_init+0x5b/0x170 [kvm]
+ ...
+ Allocated by task 16817:
+ __kmem_cache_create_args+0x12c/0x3b0
+ __kmem_cache_create.constprop.0+0xb6/0xf0 [kvm]
+ kvm_mmu_vendor_module_init+0x13b/0x170 [kvm]
+ ...
+ Freed by task 16820:
+ kmem_cache_destroy+0x117/0x1d0
+ kvm_mmu_vendor_module_exit+0x21/0x30 [kvm]
+
+Clear both pointers immediately after destroying their caches so that
+the stored state reflects the caches' lifetime and repeated cleanup is
+safe.
+
+With the fix applied, the same injected vendor module reload fails with
+-ENOMEM as expected and produces no KASAN report.
+
+Fixes: cb498ea2ce1d ("KVM: Portability: Combine kvm_init and kvm_init_x86")
+Cc: stable@vger.kernel.org
+Signed-off-by: Phil Rosenthal <phil@phil.gs>
+Message-ID: <20260718-kvm-mmu-cache-uaf-v3-1-e103b93c74e1@phil.gs>
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/kvm/mmu/mmu.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/arch/x86/kvm/mmu/mmu.c
++++ b/arch/x86/kvm/mmu/mmu.c
+@@ -7502,7 +7502,9 @@ void kvm_mmu_invalidate_mmio_sptes(struc
+ static void mmu_destroy_caches(void)
+ {
+ kmem_cache_destroy(pte_list_desc_cache);
++ pte_list_desc_cache = NULL;
+ kmem_cache_destroy(mmu_page_header_cache);
++ mmu_page_header_cache = NULL;
+ }
+
+ static void kvm_wake_nx_recovery_thread(struct kvm *kvm)
--- /dev/null
+From e800decd9c0ac4349bcd8f8f9b29fd21fe93165e Mon Sep 17 00:00:00 2001
+From: Venkatesh Srinivas <venkateshs@chromium.org>
+Date: Wed, 15 Jul 2026 23:42:35 +0000
+Subject: KVM: x86: Only reset TSC Deadline Timer in apic_timer_expired on KVM_RUN
+
+From: Venkatesh Srinivas <venkateshs@chromium.org>
+
+commit e800decd9c0ac4349bcd8f8f9b29fd21fe93165e upstream.
+
+On Intel platforms with a VMX preemption timer and APICv, if a VMM
+calls KVM_GET_LAPIC before KVM_GET_MSRS to save the vCPU state, it is
+possible to lose a pending timer interrupt.
+
+If the thread running these ioctls is migrated to another core after
+calling KVM_GET_LAPIC but before KVM_GET_MSRS and the guest is using
+their LAPIC timer in TSC-deadline mode, not only does the save LAPIC
+state not carry the pending interrupt, the TSCDEADLINE MSR will be
+zeroed.
+
+After migration across CPUs, KVM_GET_MSRS calls vcpu_load, posting the
+interrupt and clearing the MSR:
+vcpu_load() ->
+ kvm_arch_vcpu_load() ->
+ kvm_lapic_restart_hv_timer() ->
+ start_hv_timer() ->
+ apic_timer_expired() ->
+ kvm_apic_inject_pending_timer_irqs()
+ . post interrupt into the LAPIC state
+ . clear IA32_TSCDEADLINE
+
+The saved LAPIC state will be missing the pending interrupt and the saved
+MSR will be zero. Oops.
+
+Fix by only posting an interrupt when we're attempting to enter the guest
+(vcpu->wants_to_run == true), not for vcpu_load from other paths.
+
+Assisted-by: gemini:gemini-3.1-pro-preview
+Debugged-by: David Matlack <dmatlack@google.com>
+Debugged-by: Sean Christopherson <seanjc@google.com>
+Debugged-by: Jim Mattson <jmattson@google.com>
+Debugged-by: James Houghton <jthoughton@google.com>
+Signed-off-by: Venkatesh Srinivas <venkateshs@chromium.org>
+Message-ID: <20260715234234.15382-2-venkateshs@chromium.org>
+Reviewed-by: James Houghton <jthoughton@google.com>
+Reviewed-by: Chao Gao <chao.gao@intel.com>
+Cc: stable@vger.kernel.org
+Fixes: ae95f566b3d2 ("KVM: X86: TSCDEADLINE MSR emulation fastpath", 2020-05-15)
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/x86/kvm/lapic.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/arch/x86/kvm/lapic.c
++++ b/arch/x86/kvm/lapic.c
+@@ -2052,7 +2052,7 @@ static void apic_timer_expired(struct kv
+ if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use)
+ ktimer->expired_tscdeadline = ktimer->tscdeadline;
+
+- if (!from_timer_fn && apic->apicv_active) {
++ if (!from_timer_fn && apic->apicv_active && vcpu->wants_to_run) {
+ WARN_ON(kvm_get_running_vcpu() != vcpu);
+ kvm_apic_inject_pending_timer_irqs(apic);
+ return;
--- /dev/null
+From d024a0a7879e6f37c0152aacf6d8e37b214a1738 Mon Sep 17 00:00:00 2001
+From: Xie Bo <xb@ultrarisc.com>
+Date: Wed, 15 Jul 2026 10:03:59 +0800
+Subject: RISC-V: KVM: Serialize virtual interrupt pending state updates
+
+From: Xie Bo <xb@ultrarisc.com>
+
+commit d024a0a7879e6f37c0152aacf6d8e37b214a1738 upstream.
+
+KVM RISC-V tracks guest local interrupt state with two bitmaps:
+
+ - irqs_pending: interrupts that should be visible to the guest
+ - irqs_pending_mask: interrupts whose pending state changed
+
+The current code updates those bitmaps with independent atomic bitops
+and assumes a multiple-producer, single-consumer protocol. That model
+does not actually hold.
+
+kvm_riscv_vcpu_sync_interrupts() is not a pure consumer. When the guest
+changes guest-visible HVIP state, sync_interrupts() writes both
+irqs_pending and irqs_pending_mask to reflect the new guest state back
+into KVM state. As a result, irqs_pending and irqs_pending_mask form a
+single logical state transition, but they are not updated atomically as
+a pair.
+
+This allows a race where a newly injected interrupt is lost. For
+example:
+
+ CPU0 CPU1
+ ---- ----
+ kvm_riscv_vcpu_set_interrupt(VS_SOFT)
+ set_bit(VS_SOFT, irqs_pending)
+ kvm_riscv_vcpu_sync_interrupts()
+ sees guest-cleared HVIP.VSSIP
+ sets irqs_pending_mask
+ clear_bit(IRQ_VS_SOFT, irqs_pending)
+ set_bit(VS_SOFT, irqs_pending_mask)
+ kvm_vcpu_kick()
+
+After that interleaving, a later flush can update HVIP without VSSIP
+even though a new virtual interrupt was injected. In practice, the
+guest can remain blocked in WFI with work pending.
+
+The same pending/mask protocol is shared by VS soft interrupts, PMU
+overflow delivery, and AIA high interrupt synchronization, so the race
+is not limited to one interrupt source.
+
+Fix this by serializing all updates to irqs_pending and irqs_pending_mask
+with a per-vCPU raw spinlock. This keeps the pending bit and the dirty
+mask as one state transition across:
+
+ - set/unset interrupt
+ - guest HVIP sync
+ - interrupt flush to guest CSR state
+ - vCPU reset
+ - AIA CSR writes that clear dirty state
+
+Use non-atomic bitmap operations while holding the lock. Hold the lock
+across the AIA sync, flush, and pending checks as well, so both bitmap
+words share the same serialization domain.
+
+This intentionally replaces the existing lockless protocol instead of
+trying to repair it with additional barriers. The problem is not memory
+ordering on a single field; it is that two separate bitmaps encode one
+shared state machine while both producers and sync paths can modify
+them. A per-vCPU raw spinlock keeps the fix small, local, and suitable
+for backporting.
+
+Fixes: cce69aff689e ("RISC-V: KVM: Implement VCPU interrupts and requests handling")
+Cc: stable@vger.kernel.org
+Signed-off-by: Xie Bo <xb@ultrarisc.com>
+Reviewed-by: Anup Patel <anup@brainfault.org>
+Link: https://lore.kernel.org/r/20260715020359.1521354-2-xb@ultrarisc.com
+Signed-off-by: Anup Patel <anup@brainfault.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ arch/riscv/include/asm/kvm_host.h | 10 ++---
+ arch/riscv/kvm/aia.c | 35 +++++++++++++++----
+ arch/riscv/kvm/vcpu.c | 68 ++++++++++++++++++++++++++------------
+ arch/riscv/kvm/vcpu_onereg.c | 8 +++-
+ 4 files changed, 87 insertions(+), 34 deletions(-)
+
+--- a/arch/riscv/include/asm/kvm_host.h
++++ b/arch/riscv/include/asm/kvm_host.h
+@@ -207,13 +207,13 @@ struct kvm_vcpu_arch {
+ /*
+ * VCPU interrupts
+ *
+- * We have a lockless approach for tracking pending VCPU interrupts
+- * implemented using atomic bitops. The irqs_pending bitmap represent
+- * pending interrupts whereas irqs_pending_mask represent bits changed
+- * in irqs_pending. Our approach is modeled around multiple producer
+- * and single consumer problem where the consumer is the VCPU itself.
++ * The irqs_pending bitmap represents pending interrupts whereas
++ * irqs_pending_mask represents bits changed in irqs_pending. Updates
++ * to these bitmaps are serialized so vcpu interrupt sync/flush cannot
++ * drop a newly injected interrupt while syncing guest-visible HVIP.
+ */
+ #define KVM_RISCV_VCPU_NR_IRQS 64
++ raw_spinlock_t irqs_pending_lock;
+ DECLARE_BITMAP(irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
+ DECLARE_BITMAP(irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
+
+--- a/arch/riscv/kvm/aia.c
++++ b/arch/riscv/kvm/aia.c
+@@ -51,12 +51,15 @@ void kvm_riscv_vcpu_aia_flush_interrupts
+ struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
+ unsigned long mask, val;
+
++ lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
++
+ if (!kvm_riscv_aia_available())
+ return;
+
+- if (READ_ONCE(vcpu->arch.irqs_pending_mask[1])) {
+- mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[1], 0);
+- val = READ_ONCE(vcpu->arch.irqs_pending[1]) & mask;
++ mask = vcpu->arch.irqs_pending_mask[1];
++ if (mask) {
++ vcpu->arch.irqs_pending_mask[1] = 0;
++ val = vcpu->arch.irqs_pending[1] & mask;
+
+ csr->hviph &= ~mask;
+ csr->hviph |= val;
+@@ -67,6 +70,8 @@ void kvm_riscv_vcpu_aia_sync_interrupts(
+ {
+ struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
+
++ lockdep_assert_held(&vcpu->arch.irqs_pending_lock);
++
+ if (kvm_riscv_aia_available())
+ csr->vsieh = ncsr_read(CSR_VSIEH);
+ }
+@@ -75,13 +80,22 @@ void kvm_riscv_vcpu_aia_sync_interrupts(
+ bool kvm_riscv_vcpu_aia_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
+ {
+ unsigned long seip;
++#ifdef CONFIG_32BIT
++ unsigned long flags;
++ bool pending;
++#endif
+
+ if (!kvm_riscv_aia_available())
+ return false;
+
+ #ifdef CONFIG_32BIT
+- if (READ_ONCE(vcpu->arch.irqs_pending[1]) &
+- (vcpu->arch.aia_context.guest_csr.vsieh & upper_32_bits(mask)))
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++ pending = vcpu->arch.irqs_pending[1] &
++ (vcpu->arch.aia_context.guest_csr.vsieh &
++ upper_32_bits(mask));
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
++
++ if (pending)
+ return true;
+ #endif
+
+@@ -205,6 +219,9 @@ int kvm_riscv_vcpu_aia_set_csr(struct kv
+ {
+ struct kvm_vcpu_aia_csr *csr = &vcpu->arch.aia_context.guest_csr;
+ unsigned long regs_max = sizeof(struct kvm_riscv_aia_csr) / sizeof(unsigned long);
++#ifdef CONFIG_32BIT
++ unsigned long flags;
++#endif
+
+ if (!riscv_isa_extension_available(vcpu->arch.isa, SSAIA))
+ return -ENOENT;
+@@ -217,8 +234,12 @@ int kvm_riscv_vcpu_aia_set_csr(struct kv
+ ((unsigned long *)csr)[reg_num] = val;
+
+ #ifdef CONFIG_32BIT
+- if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph))
+- WRITE_ONCE(vcpu->arch.irqs_pending_mask[1], 0);
++ if (reg_num == KVM_REG_RISCV_CSR_AIA_REG(siph)) {
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++ vcpu->arch.irqs_pending_mask[1] = 0;
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock,
++ flags);
++ }
+ #endif
+ }
+
+--- a/arch/riscv/kvm/vcpu.c
++++ b/arch/riscv/kvm/vcpu.c
+@@ -80,6 +80,7 @@ static void kvm_riscv_vcpu_context_reset
+
+ static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu, bool kvm_sbi_reset)
+ {
++ unsigned long flags;
+ bool loaded;
+
+ /**
+@@ -104,8 +105,10 @@ static void kvm_riscv_reset_vcpu(struct
+
+ kvm_riscv_vcpu_aia_reset(vcpu);
+
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ bitmap_zero(vcpu->arch.irqs_pending, KVM_RISCV_VCPU_NR_IRQS);
+ bitmap_zero(vcpu->arch.irqs_pending_mask, KVM_RISCV_VCPU_NR_IRQS);
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+
+ kvm_riscv_vcpu_pmu_reset(vcpu);
+
+@@ -151,6 +154,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu
+
+ /* Setup VCPU hfence queue */
+ spin_lock_init(&vcpu->arch.hfence_lock);
++ raw_spin_lock_init(&vcpu->arch.irqs_pending_lock);
+
+ spin_lock_init(&vcpu->arch.reset_state.lock);
+
+@@ -352,10 +356,14 @@ void kvm_riscv_vcpu_flush_interrupts(str
+ {
+ struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+ unsigned long mask, val;
++ unsigned long flags;
+
+- if (READ_ONCE(vcpu->arch.irqs_pending_mask[0])) {
+- mask = xchg_acquire(&vcpu->arch.irqs_pending_mask[0], 0);
+- val = READ_ONCE(vcpu->arch.irqs_pending[0]) & mask;
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++
++ mask = vcpu->arch.irqs_pending_mask[0];
++ if (mask) {
++ vcpu->arch.irqs_pending_mask[0] = 0;
++ val = vcpu->arch.irqs_pending[0] & mask;
+
+ csr->hvip &= ~mask;
+ csr->hvip |= val;
+@@ -363,11 +371,14 @@ void kvm_riscv_vcpu_flush_interrupts(str
+
+ /* Flush AIA high interrupts */
+ kvm_riscv_vcpu_aia_flush_interrupts(vcpu);
++
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+ }
+
+ void kvm_riscv_vcpu_sync_interrupts(struct kvm_vcpu *vcpu)
+ {
+ unsigned long hvip;
++ unsigned long flags;
+ struct kvm_vcpu_arch *v = &vcpu->arch;
+ struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+
+@@ -376,34 +387,41 @@ void kvm_riscv_vcpu_sync_interrupts(stru
+
+ /* Sync-up HVIP.VSSIP bit changes does by Guest */
+ hvip = ncsr_read(CSR_HVIP);
++
++ raw_spin_lock_irqsave(&v->irqs_pending_lock, flags);
++
+ if ((csr->hvip ^ hvip) & (1UL << IRQ_VS_SOFT)) {
+ if (hvip & (1UL << IRQ_VS_SOFT)) {
+- if (!test_and_set_bit(IRQ_VS_SOFT,
+- v->irqs_pending_mask))
+- set_bit(IRQ_VS_SOFT, v->irqs_pending);
++ if (!__test_and_set_bit(IRQ_VS_SOFT,
++ v->irqs_pending_mask))
++ __set_bit(IRQ_VS_SOFT, v->irqs_pending);
+ } else {
+- if (!test_and_set_bit(IRQ_VS_SOFT,
+- v->irqs_pending_mask))
+- clear_bit(IRQ_VS_SOFT, v->irqs_pending);
++ if (!__test_and_set_bit(IRQ_VS_SOFT,
++ v->irqs_pending_mask))
++ __clear_bit(IRQ_VS_SOFT, v->irqs_pending);
+ }
+ }
+
+ /* Sync up the HVIP.LCOFIP bit changes (only clear) by the guest */
+ if ((csr->hvip ^ hvip) & (1UL << IRQ_PMU_OVF)) {
+ if (!(hvip & (1UL << IRQ_PMU_OVF)) &&
+- !test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
+- clear_bit(IRQ_PMU_OVF, v->irqs_pending);
++ !__test_and_set_bit(IRQ_PMU_OVF, v->irqs_pending_mask))
++ __clear_bit(IRQ_PMU_OVF, v->irqs_pending);
+ }
+
+ /* Sync-up AIA high interrupts */
+ kvm_riscv_vcpu_aia_sync_interrupts(vcpu);
+
++ raw_spin_unlock_irqrestore(&v->irqs_pending_lock, flags);
++
+ /* Sync-up timer CSRs */
+ kvm_riscv_vcpu_timer_sync(vcpu);
+ }
+
+ int kvm_riscv_vcpu_set_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ {
++ unsigned long flags;
++
+ /*
+ * We only allow VS-mode software, timer, and external
+ * interrupts when irq is one of the local interrupts
+@@ -416,9 +434,10 @@ int kvm_riscv_vcpu_set_interrupt(struct
+ irq != IRQ_PMU_OVF)
+ return -EINVAL;
+
+- set_bit(irq, vcpu->arch.irqs_pending);
+- smp_mb__before_atomic();
+- set_bit(irq, vcpu->arch.irqs_pending_mask);
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++ __set_bit(irq, vcpu->arch.irqs_pending);
++ __set_bit(irq, vcpu->arch.irqs_pending_mask);
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+
+ kvm_vcpu_kick(vcpu);
+
+@@ -427,6 +446,8 @@ int kvm_riscv_vcpu_set_interrupt(struct
+
+ int kvm_riscv_vcpu_unset_interrupt(struct kvm_vcpu *vcpu, unsigned int irq)
+ {
++ unsigned long flags;
++
+ /*
+ * We only allow VS-mode software, timer, counter overflow and external
+ * interrupts when irq is one of the local interrupts
+@@ -439,26 +460,33 @@ int kvm_riscv_vcpu_unset_interrupt(struc
+ irq != IRQ_PMU_OVF)
+ return -EINVAL;
+
+- clear_bit(irq, vcpu->arch.irqs_pending);
+- smp_mb__before_atomic();
+- set_bit(irq, vcpu->arch.irqs_pending_mask);
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++ __clear_bit(irq, vcpu->arch.irqs_pending);
++ __set_bit(irq, vcpu->arch.irqs_pending_mask);
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+
+ return 0;
+ }
+
+ bool kvm_riscv_vcpu_has_interrupts(struct kvm_vcpu *vcpu, u64 mask)
+ {
++ unsigned long flags;
+ unsigned long ie;
++ bool ret;
+
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
+ ie = ((vcpu->arch.guest_csr.vsie & VSIP_VALID_MASK)
+ << VSIP_TO_HVIP_SHIFT) & (unsigned long)mask;
+ ie |= vcpu->arch.guest_csr.vsie & ~IRQ_LOCAL_MASK &
+ (unsigned long)mask;
+- if (READ_ONCE(vcpu->arch.irqs_pending[0]) & ie)
+- return true;
++ ret = vcpu->arch.irqs_pending[0] & ie;
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
+
+ /* Check AIA high interrupts */
+- return kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
++ if (!ret)
++ ret = kvm_riscv_vcpu_aia_has_interrupts(vcpu, mask);
++
++ return ret;
+ }
+
+ void __kvm_riscv_vcpu_power_off(struct kvm_vcpu *vcpu)
+--- a/arch/riscv/kvm/vcpu_onereg.c
++++ b/arch/riscv/kvm/vcpu_onereg.c
+@@ -276,6 +276,7 @@ static int kvm_riscv_vcpu_general_get_cs
+ {
+ struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr;
+ unsigned long regs_max = sizeof(struct kvm_riscv_csr) / sizeof(unsigned long);
++ unsigned long flags;
+
+ if (reg_num >= regs_max)
+ return -ENOENT;
+@@ -311,8 +312,11 @@ static int kvm_riscv_vcpu_general_set_cs
+
+ ((unsigned long *)csr)[reg_num] = reg_val;
+
+- if (reg_num == KVM_REG_RISCV_CSR_REG(sip))
+- WRITE_ONCE(vcpu->arch.irqs_pending_mask[0], 0);
++ if (reg_num == KVM_REG_RISCV_CSR_REG(sip)) {
++ raw_spin_lock_irqsave(&vcpu->arch.irqs_pending_lock, flags);
++ vcpu->arch.irqs_pending_mask[0] = 0;
++ raw_spin_unlock_irqrestore(&vcpu->arch.irqs_pending_lock, flags);
++ }
+
+ return 0;
+ }