[PATCH] KVM: x86/mmu: Fix double-free of mmu_page_header_cache on vendor module reload

Phil Rosenthal posted 1 patch 6 days, 14 hours ago
There is a newer version of this series
arch/x86/kvm/mmu/mmu.c | 2 ++
1 file changed, 2 insertions(+)
[PATCH] KVM: x86/mmu: Fix double-free of mmu_page_header_cache on vendor module reload
Posted by Phil Rosenthal 6 days, 14 hours ago
mmu_destroy_caches() frees pte_list_desc_cache and mmu_page_header_cache
but does not set the pointers to NULL.  Because they are static globals in
the always-resident kvm.ko, they survive a vendor module unload/reload
cycle.  If a reload of kvm_intel/kvm_amd fails because the first
kmem_cache allocation returns NULL (e.g., under memory pressure), the
error path calls mmu_destroy_caches() again.  On that second call,
mmu_page_header_cache still points to the already-freed cache, resulting
in a double-free.

Concrete trigger:
  1. Load kvm.ko and vendor module (both caches created).
  2. Unload vendor module [leave kvm.ko loaded] (caches freed, pointers left dangling).
  3. Reload vendor module; first cache creation fails → error path
     double-frees mmu_page_header_cache.

Fix by resetting both pointers to NULL immediately after destroying the
caches, making the cleanup idempotent.

The bug was verified on a KASAN-enabled kernel by instrumenting
kvm_mmu_vendor_module_init() to force the first allocation to fail on a
second load, simulating OOM; KASAN reported:

  BUG: KASAN: slab-use-after-free in kvm_mmu_vendor_module_init+0x1ba/0x1e0 [kvm]
  ...
  Allocated by task 11693:   <- first load
  Freed by task 11696:       <- previous unload

Signed-off-by: Phil Rosenthal <phil@phil.gs>
Tested-by: Phil Rosenthal <phil@phil.gs>
---
 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