From 411bc6b502ee543b8fd5ddd2ed8fff8072b8425d Mon Sep 17 00:00:00 2001 From: Andrew Lukoshko Date: Thu, 2 Jul 2026 19:54:17 +0000 Subject: [PATCH] Add KVM hugepage recovery memslot bounds fix (upstream ef057cbf825e); bump to 553.139.4 --- ...86-mmu-ensure-hugepage-is-in-by-slot.patch | 134 ++++++++++++++++++ SPECS/kernel.spec | 10 +- 2 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 SOURCES/1101-kvm-x86-mmu-ensure-hugepage-is-in-by-slot.patch diff --git a/SOURCES/1101-kvm-x86-mmu-ensure-hugepage-is-in-by-slot.patch b/SOURCES/1101-kvm-x86-mmu-ensure-hugepage-is-in-by-slot.patch new file mode 100644 index 000000000..b012af26b --- /dev/null +++ b/SOURCES/1101-kvm-x86-mmu-ensure-hugepage-is-in-by-slot.patch @@ -0,0 +1,134 @@ +From: Paolo Bonzini +Date: Fri, 26 Jun 2026 19:46:19 +0200 +Subject: KVM: x86/mmu: Ensure hugepage is in by slot before checking max mapping level + +From: Sean Christopherson + +commit ef057cbf825e03b63f6edf5980f96abf3c53089d upstream. + +When recovering hugepages in the shadow MMU, verify that the base gfn of +the shadow page is actually contained within the target memslot, *before* +querying the max mapping level given the shadow page's gfn. Failure to +pre-check the validity of the gfn can lead to an out-of-bounds access to +the slot's lpage_info (which typically manifests as a host #PF because the +lpage_info is vmalloc'd) if the guest creates a hugepage mapping (in its +PTEs) that extends "below" the bounds of a memslot. + +When faulting in memory for a guest, and the size of the guest mapping is +greater than KVM's (current) max mapping, then KVM will create a "direct" +shadow page (direct in that there are no gPTEs to shadow, and so the target +gfn is a direct calculation given the base gfn of the shadow page). The +hugepage recovery flow looks for such direct shadow pages, as forcing 4KiB +mappings when dirty logging generates the guest > host mapping size case. +When the 4KiB restriction is lifted, then KVM can replace the shadow page +with a hugepage. + +But if KVM originally used a smaller mapping than the guest because the +range of memory covered by the guest hugepage exceeds the bounds of a +memslot, then KVM will link a direct shadow page with a gfn that is outside +the bounds of the memslot being used to fault in memory. The rmap entry +added for the leaf mapping is correct and within bounds, but the gfn of the +leaf SPTE's parent shadow page will be out of bounds. + + BUG: unable to handle page fault for address: ffffc90000806ffc + #PF: supervisor read access in kernel mode + #PF: error_code(0x0000) - not-present page + PGD 100000067 P4D 100000067 PUD 1002a7067 PMD 10612f067 PTE 0 + Oops: Oops: 0000 [#1] SMP + CPU: 13 UID: 1000 PID: 757 Comm: mmu_stress_test Not tainted 7.1.0-rc1-48ce1e26eace-x86_pir_to_irr_comments-vm #341 PREEMPT + Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 0.0.0 02/06/2015 + RIP: 0010:kvm_mmu_max_mapping_level+0x79/0x2b0 [kvm] + Call Trace: + + kvm_mmu_recover_huge_pages+0x21b/0x320 [kvm] + kvm_set_memslot+0x1ee/0x590 [kvm] + kvm_set_memory_region.part.0+0x3a1/0x4d0 [kvm] + kvm_vm_ioctl+0x9bf/0x15d0 [kvm] + __x64_sys_ioctl+0x8a/0xd0 + do_syscall_64+0xb7/0xbb0 + entry_SYSCALL_64_after_hwframe+0x4b/0x53 + RIP: 0033:0x7f21c0f1a9bf + + +Don't bother pre-checking the bounds of the potential hugepage, i.e. don't +check that e.g. sp->gfn + KVM_PAGES_PER_HPAGE(sp->role.level + 1) is also +within the memslot, as the checks performed by kvm_mmu_max_mapping_level() +are a superset of the basic bounds checks. I.e. pre-checking the full +range would be a dubious micro-optimization. + +Fixes: 9eba50f8d7fc ("KVM: x86/mmu: Consult max mapping level when zapping collapsible SPTEs") +Cc: stable@vger.kernel.org +Cc: David Matlack +Cc: James Houghton +Cc: Alexander Bulekov +Cc: Fred Griffoul +Cc: Alexander Graf +Cc: David Woodhouse +Cc: Filippo Sironi +Cc: Ivan Orlov +Signed-off-by: Sean Christopherson +Signed-off-by: Paolo Bonzini +--- + arch/x86/kvm/mmu/mmu.c | 19 +++++++++++++------ + include/linux/kvm_host.h | 7 ++++++- + 2 files changed, 19 insertions(+), 7 deletions(-) + +diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c +index 9597112..12d3e1f 100644 +--- a/arch/x86/kvm/mmu/mmu.c ++++ b/arch/x86/kvm/mmu/mmu.c +@@ -6045,13 +6045,20 @@ restart: + pfn = spte_to_pfn(*sptep); + + /* +- * We cannot do huge page mapping for indirect shadow pages, +- * which are found on the last rmap (level = 1) when not using +- * tdp; such shadow pages are synced with the page table in +- * the guest, and the guest page table is using 4K page size +- * mapping if the indirect sp has level = 1. ++ * Direct shadow page can be replaced by a hugepage if the host ++ * mapping level allows it and the memslot maps all of the host ++ * hugepage. Note! If the memslot maps only part of the ++ * hugepage, sp->gfn may be below slot->base_gfn, and querying ++ * the max mapping level would cause an out-of-bounds lpage_info ++ * access. So the gfn bounds check *must* be done first. ++ * ++ * Indirect shadow pages are created when the guest page tables ++ * are using 4K pages. Since the host mapping is always ++ * constrained by the page size in the guest, indirect shadow ++ * pages are never collapsible. + */ +- if (sp->role.direct && !kvm_is_reserved_pfn(pfn) && ++ if (sp->role.direct && is_gfn_in_memslot(slot, sp->gfn) && ++ !kvm_is_reserved_pfn(pfn) && + sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn, + pfn, PG_LEVEL_NUM)) { + pte_list_remove(kvm, rmap_head, sptep); +diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h +index eaebea6..c37570a 100644 +--- a/include/linux/kvm_host.h ++++ b/include/linux/kvm_host.h +@@ -1378,6 +1378,11 @@ static inline struct rcuwait *kvm_arch_vcpu_get_wait(struct kvm_vcpu *vcpu) + #endif + } + ++static inline bool is_gfn_in_memslot(const struct kvm_memory_slot *slot, gfn_t gfn) ++{ ++ return gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages; ++} ++ + /* + * Wake a vCPU if necessary, but don't do any stats/metadata updates. Returns + * true if the vCPU was blocking and was awakened, false otherwise. +@@ -1464,7 +1469,7 @@ try_get_memslot(struct kvm_memory_slot *slot, gfn_t gfn) + if (!slot) + return NULL; + +- if (gfn >= slot->base_gfn && gfn < slot->base_gfn + slot->npages) ++ if (is_gfn_in_memslot(slot, gfn)) + return slot; + else + return NULL; +-- +2.43.0 + diff --git a/SPECS/kernel.spec b/SPECS/kernel.spec index 9be1b8dae..8ed4eb4a2 100644 --- a/SPECS/kernel.spec +++ b/SPECS/kernel.spec @@ -49,11 +49,11 @@ # define buildid .local %define specversion 4.18.0 -%define pkgrelease 553.139.3.el8_10 +%define pkgrelease 553.139.4.el8_10 %define tarfile_release 553.139.1.el8_10 # allow pkg_release to have configurable %%{?dist} tag -%define specrelease 553.139.3%{?dist} +%define specrelease 553.139.4%{?dist} %define pkg_release %{specrelease}%{?buildid} @@ -554,6 +554,7 @@ Patch2008: 0008-Bring-back-deprecated-pci-ids-to-be2iscsi-driver.patch # AlmaLinux ahead-of-RHEL security fixes Patch1100: 1100-kvm-x86-fix-shadow-paging-use-after-free.patch +Patch1101: 1101-kvm-x86-mmu-ensure-hugepage-is-in-by-slot.patch # END OF PATCH DEFINITIONS @@ -1134,6 +1135,7 @@ ApplyPatch 0008-Bring-back-deprecated-pci-ids-to-be2iscsi-driver.patch # AlmaLinux ahead-of-RHEL security fixes ApplyPatch 1100-kvm-x86-fix-shadow-paging-use-after-free.patch +ApplyPatch 1101-kvm-x86-mmu-ensure-hugepage-is-in-by-slot.patch # END OF PATCH APPLICATIONS @@ -2736,6 +2738,10 @@ fi # # %changelog +* Thu Jul 02 2026 Andrei Lukoshko - 4.18.0-553.139.4 +- Ensure hugepage is in by slot before checking max mapping level in KVM + hugepage recovery, upstream ef057cbf825e (1101) + * Thu Jul 02 2026 Andrei Lukoshko - 4.18.0-553.139.3 - Replace CVE-2026-46113 backport patch series with a single combined adaptation of upstream 0cb2af2ea66a and 81ccda30b4e8 (1100)