]> git.proxmox.com Git - pve-kernel.git/blame - patches/kernel/0009-KVM-x86-mmu-Fix-an-sign-extension-bug-with-mmu_seq-t.patch
rebase patches on top of Ubuntu-6.2.0-36.36
[pve-kernel.git] / patches / kernel / 0009-KVM-x86-mmu-Fix-an-sign-extension-bug-with-mmu_seq-t.patch
CommitLineData
6810c247
FE
1From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
2From: Sean Christopherson <seanjc@google.com>
3Date: Wed, 23 Aug 2023 18:01:04 -0700
4Subject: [PATCH] KVM: x86/mmu: Fix an sign-extension bug with mmu_seq that
5 hangs vCPUs
6MIME-Version: 1.0
7Content-Type: text/plain; charset=UTF-8
8Content-Transfer-Encoding: 8bit
9
10Upstream commit ba6e3fe25543 ("KVM: x86/mmu: Grab mmu_invalidate_seq in
11kvm_faultin_pfn()") unknowingly fixed the bug in v6.3 when refactoring
12how KVM tracks the sequence counter snapshot.
13
14Take the vCPU's mmu_seq snapshot as an "unsigned long" instead of an "int"
15when checking to see if a page fault is stale, as the sequence count is
16stored as an "unsigned long" everywhere else in KVM. This fixes a bug
17where KVM will effectively hang vCPUs due to always thinking page faults
18are stale, which results in KVM refusing to "fix" faults.
19
20mmu_invalidate_seq (née mmu_notifier_seq) is a sequence counter used when
21KVM is handling page faults to detect if userspace mappings relevant to
22the guest were invalidated between snapshotting the counter and acquiring
23mmu_lock, i.e. to ensure that the userspace mapping KVM is using to
24resolve the page fault is fresh. If KVM sees that the counter has
25changed, KVM simply resumes the guest without fixing the fault.
26
27What _should_ happen is that the source of the mmu_notifier invalidations
28eventually goes away, mmu_invalidate_seq becomes stable, and KVM can once
29again fix guest page fault(s).
30
31But for a long-lived VM and/or a VM that the host just doesn't particularly
32like, it's possible for a VM to be on the receiving end of 2 billion (with
33a B) mmu_notifier invalidations. When that happens, bit 31 will be set in
34mmu_invalidate_seq. This causes the value to be turned into a 32-bit
35negative value when implicitly cast to an "int" by is_page_fault_stale(),
36and then sign-extended into a 64-bit unsigned when the signed "int" is
37implicitly cast back to an "unsigned long" on the call to
38mmu_invalidate_retry_hva().
39
40As a result of the casting and sign-extension, given a sequence counter of
41e.g. 0x8002dc25, mmu_invalidate_retry_hva() ends up doing
42
43 if (0x8002dc25 != 0xffffffff8002dc25)
44
45and signals that the page fault is stale and needs to be retried even
46though the sequence counter is stable, and KVM effectively hangs any vCPU
47that takes a page fault (EPT violation or #NPF when TDP is enabled).
48
49Reported-by: Brian Rak <brak@vultr.com>
50Reported-by: Amaan Cheval <amaan.cheval@gmail.com>
51Reported-by: Eric Wheeler <kvm@lists.ewheeler.net>
52Closes: https://lore.kernel.org/all/f023d927-52aa-7e08-2ee5-59a2fbc65953@gameservers.com
53Fixes: a955cad84cda ("KVM: x86/mmu: Retry page fault if root is invalidated by memslot update")
54Signed-off-by: Sean Christopherson <seanjc@google.com>
55Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
56(cherry-picked from commit 82d811ff566594de3676f35808e8a9e19c5c864c in stable v6.1.51)
57Signed-off-by: Fiona Ebner <f.ebner@proxmox.com>
58---
59 arch/x86/kvm/mmu/mmu.c | 3 ++-
60 1 file changed, 2 insertions(+), 1 deletion(-)
61
62diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c
63index 3220c1285984..c42ba5cde7a4 100644
64--- a/arch/x86/kvm/mmu/mmu.c
65+++ b/arch/x86/kvm/mmu/mmu.c
66@@ -4261,7 +4261,8 @@ static int kvm_faultin_pfn(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault)
67 * root was invalidated by a memslot update or a relevant mmu_notifier fired.
68 */
69 static bool is_page_fault_stale(struct kvm_vcpu *vcpu,
70- struct kvm_page_fault *fault, int mmu_seq)
71+ struct kvm_page_fault *fault,
72+ unsigned long mmu_seq)
73 {
74 struct kvm_mmu_page *sp = to_shadow_page(vcpu->arch.mmu->root.hpa);
75