From: Greg Kroah-Hartman Date: Mon, 4 Apr 2022 14:03:26 +0000 (+0200) Subject: 5.16-stable patches X-Git-Tag: v5.17.2~39 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9907dbd79d0f30ef217e9ae46096d1067d3425ca;p=thirdparty%2Fkernel%2Fstable-queue.git 5.16-stable patches added patches: kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch --- diff --git a/queue-5.16/kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch b/queue-5.16/kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch new file mode 100644 index 00000000000..3bcd21fbb8c --- /dev/null +++ b/queue-5.16/kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch @@ -0,0 +1,152 @@ +From 2a8859f373b0a86f0ece8ec8312607eacf12485d Mon Sep 17 00:00:00 2001 +From: Paolo Bonzini +Date: Tue, 29 Mar 2022 12:56:24 -0400 +Subject: KVM: x86/mmu: do compare-and-exchange of gPTE via the user address + +From: Paolo Bonzini + +commit 2a8859f373b0a86f0ece8ec8312607eacf12485d upstream. + +FNAME(cmpxchg_gpte) is an inefficient mess. It is at least decent if it +can go through get_user_pages_fast(), but if it cannot then it tries to +use memremap(); that is not just terribly slow, it is also wrong because +it assumes that the VM_PFNMAP VMA is contiguous. + +The right way to do it would be to do the same thing as +hva_to_pfn_remapped() does since commit add6a0cd1c5b ("KVM: MMU: try to +fix up page faults before giving up", 2016-07-05), using follow_pte() +and fixup_user_fault() to determine the correct address to use for +memremap(). To do this, one could for example extract hva_to_pfn() +for use outside virt/kvm/kvm_main.c. But really there is no reason to +do that either, because there is already a perfectly valid address to +do the cmpxchg() on, only it is a userspace address. That means doing +user_access_begin()/user_access_end() and writing the code in assembly +to handle exceptions correctly. Worse, the guest PTE can be 8-byte +even on i686 so there is the extra complication of using cmpxchg8b to +account for. But at least it is an efficient mess. + +(Thanks to Linus for suggesting improvement on the inline assembly). + +Reported-by: Qiuhao Li +Reported-by: Gaoning Pan +Reported-by: Yongkang Jia +Reported-by: syzbot+6cde2282daa792c49ab8@syzkaller.appspotmail.com +Debugged-by: Tadeusz Struk +Tested-by: Maxim Levitsky +Cc: stable@vger.kernel.org +Fixes: bd53cb35a3e9 ("X86/KVM: Handle PFNs outside of kernel reach when touching GPTEs") +Signed-off-by: Paolo Bonzini +Signed-off-by: Greg Kroah-Hartman +--- + arch/x86/kvm/mmu/paging_tmpl.h | 77 +++++++++++++++++++---------------------- + 1 file changed, 37 insertions(+), 40 deletions(-) + +--- a/arch/x86/kvm/mmu/paging_tmpl.h ++++ b/arch/x86/kvm/mmu/paging_tmpl.h +@@ -34,9 +34,8 @@ + #define PT_HAVE_ACCESSED_DIRTY(mmu) true + #ifdef CONFIG_X86_64 + #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL +- #define CMPXCHG cmpxchg ++ #define CMPXCHG "cmpxchgq" + #else +- #define CMPXCHG cmpxchg64 + #define PT_MAX_FULL_LEVELS 2 + #endif + #elif PTTYPE == 32 +@@ -52,7 +51,7 @@ + #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT + #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT + #define PT_HAVE_ACCESSED_DIRTY(mmu) true +- #define CMPXCHG cmpxchg ++ #define CMPXCHG "cmpxchgl" + #elif PTTYPE == PTTYPE_EPT + #define pt_element_t u64 + #define guest_walker guest_walkerEPT +@@ -65,7 +64,9 @@ + #define PT_GUEST_DIRTY_SHIFT 9 + #define PT_GUEST_ACCESSED_SHIFT 8 + #define PT_HAVE_ACCESSED_DIRTY(mmu) ((mmu)->ept_ad) +- #define CMPXCHG cmpxchg64 ++ #ifdef CONFIG_X86_64 ++ #define CMPXCHG "cmpxchgq" ++ #endif + #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL + #else + #error Invalid PTTYPE value +@@ -147,43 +148,39 @@ static int FNAME(cmpxchg_gpte)(struct kv + pt_element_t __user *ptep_user, unsigned index, + pt_element_t orig_pte, pt_element_t new_pte) + { +- int npages; +- pt_element_t ret; +- pt_element_t *table; +- struct page *page; +- +- npages = get_user_pages_fast((unsigned long)ptep_user, 1, FOLL_WRITE, &page); +- if (likely(npages == 1)) { +- table = kmap_atomic(page); +- ret = CMPXCHG(&table[index], orig_pte, new_pte); +- kunmap_atomic(table); +- +- kvm_release_page_dirty(page); +- } else { +- struct vm_area_struct *vma; +- unsigned long vaddr = (unsigned long)ptep_user & PAGE_MASK; +- unsigned long pfn; +- unsigned long paddr; +- +- mmap_read_lock(current->mm); +- vma = find_vma_intersection(current->mm, vaddr, vaddr + PAGE_SIZE); +- if (!vma || !(vma->vm_flags & VM_PFNMAP)) { +- mmap_read_unlock(current->mm); +- return -EFAULT; +- } +- pfn = ((vaddr - vma->vm_start) >> PAGE_SHIFT) + vma->vm_pgoff; +- paddr = pfn << PAGE_SHIFT; +- table = memremap(paddr, PAGE_SIZE, MEMREMAP_WB); +- if (!table) { +- mmap_read_unlock(current->mm); +- return -EFAULT; +- } +- ret = CMPXCHG(&table[index], orig_pte, new_pte); +- memunmap(table); +- mmap_read_unlock(current->mm); +- } ++ int r = -EFAULT; ++ ++ if (!user_access_begin(ptep_user, sizeof(pt_element_t))) ++ return -EFAULT; ++ ++#ifdef CMPXCHG ++ asm volatile("1:" LOCK_PREFIX CMPXCHG " %[new], %[ptr]\n" ++ "mov $0, %[r]\n" ++ "setnz %b[r]\n" ++ "2:" ++ _ASM_EXTABLE_UA(1b, 2b) ++ : [ptr] "+m" (*ptep_user), ++ [old] "+a" (orig_pte), ++ [r] "+q" (r) ++ : [new] "r" (new_pte) ++ : "memory"); ++#else ++ asm volatile("1:" LOCK_PREFIX "cmpxchg8b %[ptr]\n" ++ "movl $0, %[r]\n" ++ "jz 2f\n" ++ "incl %[r]\n" ++ "2:" ++ _ASM_EXTABLE_UA(1b, 2b) ++ : [ptr] "+m" (*ptep_user), ++ [old] "+A" (orig_pte), ++ [r] "+rm" (r) ++ : [new_lo] "b" ((u32)new_pte), ++ [new_hi] "c" ((u32)(new_pte >> 32)) ++ : "memory"); ++#endif + +- return (ret != orig_pte); ++ user_access_end(); ++ return r; + } + + static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu, diff --git a/queue-5.16/series b/queue-5.16/series index a53dc013729..8020dbe42ce 100644 --- a/queue-5.16/series +++ b/queue-5.16/series @@ -1010,3 +1010,4 @@ n64cart-convert-bi_disk-to-bi_bdev-bd_disk-fix-build.patch revert-nbd-fix-possible-overflow-on-first_minor-in-nbd_dev_add.patch mmc-rtsx-let-mmc-core-handle-runtime-pm.patch mmc-rtsx-fix-build-errors-warnings-for-unused-variable.patch +kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch