From 68a5b68746233a71cdfa9e3a4b4b69b17b780676 Mon Sep 17 00:00:00 2001 From: almalinux-bot-kernel Date: Sat, 11 Jul 2026 05:07:26 +0000 Subject: [PATCH] Import of kernel-4.18.0-553.141.2.el8_10 --- arch/x86/kvm/mmu/mmu.c | 91 ++++++++++++++++++++++++++-------- arch/x86/kvm/mmu/paging_tmpl.h | 40 ++++++++++++++- include/linux/kvm_host.h | 7 ++- kernel.sbat | 2 +- kernel/locking/rtmutex.c | 14 ++++-- kernel/locking/rtmutex_api.c | 2 +- 6 files changed, 127 insertions(+), 29 deletions(-) diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index c4854dc023..578d6f6ee9 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -2002,21 +2002,13 @@ static void clear_sp_write_flooding_count(u64 *spte) __clear_sp_write_flooding_count(sptep_to_sp(spte)); } -static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, - gfn_t gfn, - gva_t gaddr, - unsigned level, - int direct, - unsigned int access) +static union kvm_mmu_page_role kvm_mmu_child_role(struct kvm_vcpu *vcpu, + gva_t gaddr, unsigned level, + int direct, + unsigned int access) { - bool direct_mmu = vcpu->arch.mmu->direct_map; union kvm_mmu_page_role role; - struct hlist_head *sp_list; unsigned quadrant; - struct kvm_mmu_page *sp; - int ret; - int collisions = 0; - LIST_HEAD(invalid_list); role = vcpu->arch.mmu->mmu_role.base; role.level = level; @@ -2028,6 +2020,46 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, role.quadrant = quadrant; } + return role; +} + +/* + * Returns true if @sptep already links to a present, non-large shadow page + * that matches @gfn and @role, i.e. it is safe to keep walking through the + * existing child instead of replacing it. The gfn of a direct shadow page + * only tracks its *first* mapping, so an intervening guest PTE change can + * leave a present SPTE pointing at a child that was allocated for a + * different gfn and/or role; blindly reusing it leads to a stale rmap entry + * (and eventually a use-after-free) once the mismatched child is zapped. + */ +static bool shadow_page_role_matches(u64 *sptep, gfn_t gfn, + union kvm_mmu_page_role role) +{ + struct kvm_mmu_page *child; + + if (!is_shadow_present_pte(*sptep) || is_large_pte(*sptep)) + return false; + + child = to_shadow_page(*sptep & PT64_BASE_ADDR_MASK); + return child->gfn == gfn && child->role.word == role.word; +} + +static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, + gfn_t gfn, + gva_t gaddr, + unsigned level, + int direct, + unsigned int access) +{ + bool direct_mmu = vcpu->arch.mmu->direct_map; + union kvm_mmu_page_role role = kvm_mmu_child_role(vcpu, gaddr, level, + direct, access); + struct hlist_head *sp_list; + struct kvm_mmu_page *sp; + int ret; + int collisions = 0; + LIST_HEAD(invalid_list); + sp_list = &vcpu->kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]; for_each_valid_sp(vcpu->kvm, sp, sp_list) { if (sp->gfn != gfn) { @@ -2946,8 +2978,20 @@ static int __direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) break; drop_large_spte(vcpu, it.sptep); - if (is_shadow_present_pte(*it.sptep)) - continue; + if (is_shadow_present_pte(*it.sptep)) { + union kvm_mmu_page_role role; + LIST_HEAD(invalid_list); + + role = kvm_mmu_child_role(vcpu, it.addr, it.level - 1, + true, ACC_ALL); + if (shadow_page_role_matches(it.sptep, base_gfn, role)) + continue; + + mmu_page_zap_pte(vcpu->kvm, sptep_to_sp(it.sptep), + it.sptep, &invalid_list); + kvm_mmu_remote_flush_or_zap(vcpu->kvm, &invalid_list, + true); + } sp = kvm_mmu_get_page(vcpu, base_gfn, it.addr, it.level - 1, true, ACC_ALL); @@ -5999,13 +6043,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/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index 3b825e60a7..911dbe698d 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -676,10 +676,28 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, clear_sp_write_flooding_count(it.sptep); drop_large_spte(vcpu, it.sptep); + table_gfn = gw->table_gfn[it.level - 2]; + access = gw->pt_access[it.level - 2]; + + if (is_shadow_present_pte(*it.sptep)) { + union kvm_mmu_page_role role; + LIST_HEAD(invalid_list); + + role = kvm_mmu_child_role(vcpu, fault->addr, + it.level - 1, false, access); + if (!shadow_page_role_matches(it.sptep, table_gfn, + role)) { + mmu_page_zap_pte(vcpu->kvm, + sptep_to_sp(it.sptep), + it.sptep, &invalid_list); + kvm_mmu_remote_flush_or_zap(vcpu->kvm, + &invalid_list, + true); + } + } + sp = NULL; if (!is_shadow_present_pte(*it.sptep)) { - table_gfn = gw->table_gfn[it.level - 2]; - access = gw->pt_access[it.level - 2]; sp = kvm_mmu_get_page(vcpu, table_gfn, fault->addr, it.level-1, false, access); /* @@ -736,6 +754,24 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, drop_large_spte(vcpu, it.sptep); + if (is_shadow_present_pte(*it.sptep)) { + union kvm_mmu_page_role role; + LIST_HEAD(invalid_list); + + role = kvm_mmu_child_role(vcpu, fault->addr, + it.level - 1, true, + direct_access); + if (!shadow_page_role_matches(it.sptep, base_gfn, + role)) { + mmu_page_zap_pte(vcpu->kvm, + sptep_to_sp(it.sptep), + it.sptep, &invalid_list); + kvm_mmu_remote_flush_or_zap(vcpu->kvm, + &invalid_list, + true); + } + } + if (!is_shadow_present_pte(*it.sptep)) { sp = kvm_mmu_get_page(vcpu, base_gfn, fault->addr, it.level - 1, true, direct_access); diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index eaebea6426..c37570a3ac 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; diff --git a/kernel.sbat b/kernel.sbat index d7c841558a..6ab49c6860 100644 --- a/kernel.sbat +++ b/kernel.sbat @@ -1,2 +1,2 @@ sbat,1,SBAT Version,sbat,1,https://github.com/rhboot/shim/blob/main/SBAT.md -kernel.centos,1,Red Hat,kernel-core,4.18.0-553.141.1.el8.x86_64,mailto:secalert@redhat.com +kernel.centos,1,Red Hat,kernel-core,4.18.0-553.141.2.el8.x86_64,mailto:secalert@redhat.com diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c index 95db3bb1a2..469d43de1d 100644 --- a/kernel/locking/rtmutex.c +++ b/kernel/locking/rtmutex.c @@ -1501,20 +1501,26 @@ static bool rtmutex_spin_on_owner(struct rt_mutex_base *lock, * * Must be called with lock->wait_lock held and interrupts disabled. It must * have just failed to try_to_take_rt_mutex(). + * + * When invoked from rt_mutex_start_proxy_lock() waiter::task != current ! */ static void __sched remove_waiter(struct rt_mutex_base *lock, struct rt_mutex_waiter *waiter) { bool is_top_waiter = (waiter == rt_mutex_top_waiter(lock)); struct task_struct *owner = rt_mutex_owner(lock); + struct task_struct *waiter_task = waiter->task; struct rt_mutex_base *next_lock; lockdep_assert_held(&lock->wait_lock); - raw_spin_lock(¤t->pi_lock); + if (!waiter_task) /* never enqueued */ + return; + + raw_spin_lock(&waiter_task->pi_lock); rt_mutex_dequeue(lock, waiter); - current->pi_blocked_on = NULL; - raw_spin_unlock(¤t->pi_lock); + waiter_task->pi_blocked_on = NULL; + raw_spin_unlock(&waiter_task->pi_lock); /* * Only update priority if the waiter was the highest priority @@ -1550,7 +1556,7 @@ static void __sched remove_waiter(struct rt_mutex_base *lock, raw_spin_unlock_irq(&lock->wait_lock); rt_mutex_adjust_prio_chain(owner, RT_MUTEX_MIN_CHAINWALK, lock, - next_lock, NULL, current); + next_lock, NULL, waiter_task); raw_spin_lock_irq(&lock->wait_lock); } diff --git a/kernel/locking/rtmutex_api.c b/kernel/locking/rtmutex_api.c index 56d1938cb5..c4e191340c 100644 --- a/kernel/locking/rtmutex_api.c +++ b/kernel/locking/rtmutex_api.c @@ -322,7 +322,7 @@ int __sched rt_mutex_start_proxy_lock(struct rt_mutex_base *lock, raw_spin_lock_irq(&lock->wait_lock); ret = __rt_mutex_start_proxy_lock(lock, waiter, task); - if (unlikely(ret)) + if (unlikely(ret < 0)) remove_waiter(lock, waiter); raw_spin_unlock_irq(&lock->wait_lock);