]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 4 Apr 2022 14:03:17 +0000 (16:03 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 4 Apr 2022 14:03:17 +0000 (16:03 +0200)
added patches:
kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch

queue-5.15/kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch [new file with mode: 0644]
queue-5.15/series

diff --git a/queue-5.15/kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch b/queue-5.15/kvm-x86-mmu-do-compare-and-exchange-of-gpte-via-the-user-address.patch
new file mode 100644 (file)
index 0000000..3bcd21f
--- /dev/null
@@ -0,0 +1,152 @@
+From 2a8859f373b0a86f0ece8ec8312607eacf12485d Mon Sep 17 00:00:00 2001
+From: Paolo Bonzini <pbonzini@redhat.com>
+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 <pbonzini@redhat.com>
+
+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 <qiuhao@sysec.org>
+Reported-by: Gaoning Pan <pgn@zju.edu.cn>
+Reported-by: Yongkang Jia <kangel@zju.edu.cn>
+Reported-by: syzbot+6cde2282daa792c49ab8@syzkaller.appspotmail.com
+Debugged-by: Tadeusz Struk <tadeusz.struk@linaro.org>
+Tested-by: Maxim Levitsky <mlevitsk@redhat.com>
+Cc: stable@vger.kernel.org
+Fixes: bd53cb35a3e9 ("X86/KVM: Handle PFNs outside of kernel reach when touching GPTEs")
+Signed-off-by: Paolo Bonzini <pbonzini@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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,
index 446fe9779de1c3cf9d35824877a2b8bba78d913c..8ed0ec614da356147d85583fd11a8e24bfadc34a 100644 (file)
@@ -901,3 +901,4 @@ torture-make-torture.sh-help-message-match-reality.patch
 n64cart-convert-bi_disk-to-bi_bdev-bd_disk-fix-build.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