[PATCH v2] KVM: x86/mmu: Fix double-free of mmu_page_header_cache on vendor module reload
From: Phil Rosenthal
Date: Sat Jul 18 2026 - 10:51:44 EST
mmu_destroy_caches() destroys pte_list_desc_cache and
mmu_page_header_cache, but leaves both pointers unchanged. The pointer
variables live in kvm.ko, so their stale values 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, the 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.
A concrete trigger is:
1. Load kvm.ko and a vendor module, creating both caches.
2. Unload only the vendor module, leaving kvm.ko loaded. Both caches
are destroyed, but their pointers remain dangling.
3. Reload the vendor module and fail creation of pte_list_desc_cache.
The error path attempts to destroy the stale mmu_page_header_cache.
Clear both pointers immediately after destroying their caches so that
the stored state reflects the destruction and repeated cleanup is safe.
Reproduce the issue on a KASAN-enabled v7.1.3 kernel by forcing the
second vendor module load through the cache-initialization error path.
Without the fix, KASAN reports a slab use-after-free in
kmem_cache_destroy() called from kvm_mmu_vendor_module_init(). The
allocation stack points to the initial vendor module load, while the
free stack points to kvm_mmu_vendor_module_exit() during the intervening
unload.
With the fix applied, the same injected vendor module load fails with
-ENOMEM as expected and produces no KASAN report.
Fixes: cb498ea2ce1d ("KVM: Portability: Combine kvm_init and kvm_init_x86")
Cc: stable@xxxxxxxxxxxxxxx
Tested-by: Phil Rosenthal <phil@xxxxxxx>
Signed-off-by: Phil Rosenthal <phil@xxxxxxx>
---
v2:
- Add Fixes and Cc stable trailers.
- Describe the failure as a use-after-free during repeated cache
destruction.
- Document the KASAN reproduction and fixed-kernel validation.
v1: https://lore.kernel.org/r/3FD1B35F-ECF8-476D-B2F1-6A43E6E0B641@xxxxxxx/
arch/x86/kvm/mmu/mmu.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
index 234d0a95abf5..ec49ce98a449 100644
--- a/arch/x86/kvm/mmu/mmu.c
+++ b/arch/x86/kvm/mmu/mmu.c
@@ -7574,7 +7574,9 @@ void kvm_mmu_invalidate_mmio_sptes(struct kvm *kvm, u64 gen)
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)
-- 2.47.3