Skip to content

Commit 20fbcdb

Browse files
committed
KVM: x86/mmu: Fix race condition in direct_page_fault
jira VULN-3954 cve CVE-2022-45869 commit-author Kazuki Takiguchi <takiguchi.kazuki171@gmail.com> commit 47b0c2e upstream-diff Used linux-5.15.y backport f88a697 for the clean pick. The modified function `direct_page_fault' is the same in both versions make_mmu_pages_available() must be called with mmu_lock held for write. However, if the TDP MMU is used, it will be called with mmu_lock held for read. This function does nothing unless shadow pages are used, so there is no race unless nested TDP is used. Since nested TDP uses shadow pages, old shadow pages may be zapped by this function even when the TDP MMU is enabled. Since shadow pages are never allocated by kvm_tdp_mmu_map(), a race condition can be avoided by not calling make_mmu_pages_available() if the TDP MMU is currently in use. I encountered this when repeatedly starting and stopping nested VM. It can be artificially caused by allocating a large number of nested TDP SPTEs. For example, the following BUG and general protection fault are caused in the host kernel. pte_list_remove: 00000000cd54fc10 many->many ------------[ cut here ]------------ kernel BUG at arch/x86/kvm/mmu/mmu.c:963! invalid opcode: 0000 [#1] PREEMPT SMP NOPTI RIP: 0010:pte_list_remove.cold+0x16/0x48 [kvm] Call Trace: <TASK> drop_spte+0xe0/0x180 [kvm] mmu_page_zap_pte+0x4f/0x140 [kvm] __kvm_mmu_prepare_zap_page+0x62/0x3e0 [kvm] kvm_mmu_zap_oldest_mmu_pages+0x7d/0xf0 [kvm] direct_page_fault+0x3cb/0x9b0 [kvm] kvm_tdp_page_fault+0x2c/0xa0 [kvm] kvm_mmu_page_fault+0x207/0x930 [kvm] npf_interception+0x47/0xb0 [kvm_amd] svm_invoke_exit_handler+0x13c/0x1a0 [kvm_amd] svm_handle_exit+0xfc/0x2c0 [kvm_amd] kvm_arch_vcpu_ioctl_run+0xa79/0x1780 [kvm] kvm_vcpu_ioctl+0x29b/0x6f0 [kvm] __x64_sys_ioctl+0x95/0xd0 do_syscall_64+0x5c/0x90 general protection fault, probably for non-canonical address 0xdead000000000122: 0000 [#1] PREEMPT SMP NOPTI RIP: 0010:kvm_mmu_commit_zap_page.part.0+0x4b/0xe0 [kvm] Call Trace: <TASK> kvm_mmu_zap_oldest_mmu_pages+0xae/0xf0 [kvm] direct_page_fault+0x3cb/0x9b0 [kvm] kvm_tdp_page_fault+0x2c/0xa0 [kvm] kvm_mmu_page_fault+0x207/0x930 [kvm] npf_interception+0x47/0xb0 [kvm_amd] CVE: CVE-2022-45869 Fixes: a2855af ("KVM: x86/mmu: Allow parallel page faults for the TDP MMU") Signed-off-by: Kazuki Takiguchi <takiguchi.kazuki171@gmail.com> Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini <pbonzini@redhat.com> (cherry picked from commit f88a697) Signed-off-by: Marcin Wcisło <marcin.wcislo@conclusive.pl>
1 parent bcbf5a8 commit 20fbcdb

1 file changed

Lines changed: 7 additions & 5 deletions

File tree

arch/x86/kvm/mmu/mmu.c

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2365,6 +2365,7 @@ static bool __kvm_mmu_prepare_zap_page(struct kvm *kvm,
23652365
{
23662366
bool list_unstable;
23672367

2368+
lockdep_assert_held_write(&kvm->mmu_lock);
23682369
trace_kvm_mmu_prepare_zap_page(sp);
23692370
++kvm->stat.mmu_shadow_zapped;
23702371
*nr_zapped = mmu_zap_unsync_children(kvm, sp, invalid_list);
@@ -3992,16 +3993,17 @@ static int direct_page_fault(struct kvm_vcpu *vcpu, gpa_t gpa, u32 error_code,
39923993

39933994
if (!is_noslot_pfn(pfn) && mmu_notifier_retry_hva(vcpu->kvm, mmu_seq, hva))
39943995
goto out_unlock;
3995-
r = make_mmu_pages_available(vcpu);
3996-
if (r)
3997-
goto out_unlock;
39983996

3999-
if (is_tdp_mmu_fault)
3997+
if (is_tdp_mmu_fault) {
40003998
r = kvm_tdp_mmu_map(vcpu, gpa, error_code, map_writable, max_level,
40013999
pfn, prefault);
4002-
else
4000+
} else {
4001+
r = make_mmu_pages_available(vcpu);
4002+
if (r)
4003+
goto out_unlock;
40034004
r = __direct_map(vcpu, gpa, error_code, map_writable, max_level, pfn,
40044005
prefault, is_tdp);
4006+
}
40054007

40064008
out_unlock:
40074009
if (is_tdp_mmu_fault)

0 commit comments

Comments
 (0)