From 9cb8a03ebca7263af03701ef256248ca8e5c434d Mon Sep 17 00:00:00 2001 From: Artem Savkov Date: Wed, 7 Jul 2021 17:44:40 +0200 Subject: [PATCH] KVM: do not allow mapping valid but non-reference-counted pages Kernels: 4.18.0-305.el8 4.18.0-305.3.1.el8_4 4.18.0-305.7.1.el8_4 Changes since last build: [x86_64]: kvm_main.o: changed function: __gfn_to_pfn_memslot [ppc64le]: kvm_main.o: changed function: gfn_to_pfn_memslot kvm_main.o: changed function: hva_to_pfn --------------------------- Kernels: 4.18.0-305.el8 4.18.0-305.3.1.el8_4 4.18.0-305.7.1.el8_4 4.18.0-305.10.2.el8_4 Modifications: none Z-MR: https://gitlab.com/redhat/rhel/src/kernel/rhel-8/-/merge_requests/898 Testing: kernel_write.c reproducer commit 9871aa051ac11ab8551be01d7394915a5530dbbd Author: Jon Maloy Date: Wed Jun 30 21:09:40 2021 -0400 KVM: do not allow mapping valid but non-reference-counted pages Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=1975514 Upstream: commit f8be156be163a052a067306417cd0ff679068c97 CVE: CVE-2021-22543 Brew: https://brewweb.engineering.redhat.com/brew/taskinfo?taskID=37829337 commit f8be156be163a052a067306417cd0ff679068c97 Author: Nicholas Piggin Date: Thu Jun 24 08:29:04 2021 -0400 KVM: do not allow mapping valid but non-reference-counted pages It's possible to create a region which maps valid but non-refcounted pages (e.g., tail pages of non-compound higher order allocations). These host pages can then be returned by gfn_to_page, gfn_to_pfn, etc., family of APIs, which take a reference to the page, which takes it from 0 to 1. When the reference is dropped, this will free the page incorrectly. Fix this by only taking a reference on valid pages if it was non-zero, which indicates it is participating in normal refcounting (and can be released with put_page). This addresses CVE-2021-22543. Signed-off-by: Nicholas Piggin Tested-by: Paolo Bonzini Cc: stable@vger.kernel.org Signed-off-by: Paolo Bonzini Signed-off-by: Jon Maloy Signed-off-by: Artem Savkov Acked-by: Yannick Cote Acked-by: Joe Lawrence --- virt/kvm/kvm_main.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index d4ec73c304a31..9f1cf429954c7 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -1894,6 +1894,13 @@ static bool vma_is_valid(struct vm_area_struct *vma, bool write_fault) return true; } +static int kvm_try_get_pfn(kvm_pfn_t pfn) +{ + if (kvm_is_reserved_pfn(pfn)) + return 1; + return get_page_unless_zero(pfn_to_page(pfn)); +} + static int hva_to_pfn_remapped(struct vm_area_struct *vma, unsigned long addr, bool *async, bool write_fault, bool *writable, @@ -1936,11 +1943,19 @@ static int hva_to_pfn_remapped(struct vm_area_struct *vma, * Whoever called remap_pfn_range is also going to call e.g. * unmap_mapping_range before the underlying pages are freed, * causing a call to our MMU notifier. + * + * Certain IO or PFNMAP mappings can be backed with valid + * struct pages, but be allocated without refcounting e.g., + * tail pages of non-compound higher order allocations, which + * would then underflow the refcount when the caller does the + * required put_page. Don't allow those pages here. */ - kvm_get_pfn(pfn); + if (!kvm_try_get_pfn(pfn)) + r = -EFAULT; *p_pfn = pfn; - return 0; + + return r; } /* -- 2.26.3