diff mbox series

[2/3] KVM: Check validity of offset+length of gfn_to_pfn_cache prior to activation

Message ID 20240320001542.3203871-3-seanjc@google.com (mailing list archive)
State New, archived
Headers show
Series KVM: Fix for a mostly benign gpc WARN | expand

Commit Message

Sean Christopherson March 20, 2024, 12:15 a.m. UTC
When activating a gfn_to_pfn_cache, verify that the offset+length is sane
and usable before marking the cache active.  Letting __kvm_gpc_refresh()
detect the problem results in a cache being marked active without setting
the GPA (or any other fields), which in turn results in KVM trying to
refresh a cache with INVALID_GPA.

Attempting to refresh a cache with INVALID_GPA isn't functionally
problematic, but it runs afoul of the sanity check that exactly one of
GPA or userspace HVA is valid, i.e. that a cache is either GPA-based or
HVA-based.

Reported-by: syzbot+106a4f72b0474e1d1b33@syzkaller.appspotmail.com
Closes: https://lore.kernel.org/all/0000000000005fa5cc0613f1cebd@google.com
Fixes: 721f5b0dda78 ("KVM: pfncache: allow a cache to be activated with a fixed (userspace) HVA")
Cc: David Woodhouse <dwmw2@infradead.org>
Cc: Paul Durrant <paul@xen.org>
Signed-off-by: Sean Christopherson <seanjc@google.com>
---
 virt/kvm/pfncache.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

Comments

David Woodhouse March 20, 2024, 8:20 a.m. UTC | #1
On Tue, 2024-03-19 at 17:15 -0700, Sean Christopherson wrote:
> When activating a gfn_to_pfn_cache, verify that the offset+length is sane
> and usable before marking the cache active.  Letting __kvm_gpc_refresh()
> detect the problem results in a cache being marked active without setting
> the GPA (or any other fields), which in turn results in KVM trying to
> refresh a cache with INVALID_GPA.
> 
> Attempting to refresh a cache with INVALID_GPA isn't functionally
> problematic, but it runs afoul of the sanity check that exactly one of
> GPA or userspace HVA is valid, i.e. that a cache is either GPA-based or
> HVA-based.
> 
> Reported-by: syzbot+106a4f72b0474e1d1b33@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/all/0000000000005fa5cc0613f1cebd@google.com
> Fixes: 721f5b0dda78 ("KVM: pfncache: allow a cache to be activated with a fixed (userspace) HVA")

Reviewed-by: David Woodhouse <dwmw@amazon.co.uk>
Durrant, Paul March 21, 2024, 11:11 a.m. UTC | #2
On 20/03/2024 00:15, Sean Christopherson wrote:
> When activating a gfn_to_pfn_cache, verify that the offset+length is sane
> and usable before marking the cache active.  Letting __kvm_gpc_refresh()
> detect the problem results in a cache being marked active without setting
> the GPA (or any other fields), which in turn results in KVM trying to
> refresh a cache with INVALID_GPA.
> 
> Attempting to refresh a cache with INVALID_GPA isn't functionally
> problematic, but it runs afoul of the sanity check that exactly one of
> GPA or userspace HVA is valid, i.e. that a cache is either GPA-based or
> HVA-based.
> 
> Reported-by: syzbot+106a4f72b0474e1d1b33@syzkaller.appspotmail.com
> Closes: https://lore.kernel.org/all/0000000000005fa5cc0613f1cebd@google.com
> Fixes: 721f5b0dda78 ("KVM: pfncache: allow a cache to be activated with a fixed (userspace) HVA")
> Cc: David Woodhouse <dwmw2@infradead.org>
> Cc: Paul Durrant <paul@xen.org>
> Signed-off-by: Sean Christopherson <seanjc@google.com>
> ---
>   virt/kvm/pfncache.c | 16 +++++++++-------
>   1 file changed, 9 insertions(+), 7 deletions(-)
> 

Reviewed-by: Paul Durrant <paul@xen.org>
diff mbox series

Patch

diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c
index 8f2121b5f2a0..91b0e329006b 100644
--- a/virt/kvm/pfncache.c
+++ b/virt/kvm/pfncache.c
@@ -245,8 +245,7 @@  static kvm_pfn_t hva_to_pfn_retry(struct gfn_to_pfn_cache *gpc)
 	return -EFAULT;
 }
 
-static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva,
-			     unsigned long len)
+static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long uhva)
 {
 	unsigned long page_offset;
 	bool unmap_old = false;
@@ -260,9 +259,6 @@  static int __kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned l
 	if (WARN_ON_ONCE(kvm_is_error_gpa(gpa) == kvm_is_error_hva(uhva)))
 		return -EINVAL;
 
-	if (!kvm_gpc_is_valid_len(gpa, uhva, len))
-		return -EINVAL;
-
 	lockdep_assert_held(&gpc->refresh_lock);
 
 	write_lock_irq(&gpc->lock);
@@ -365,6 +361,9 @@  int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len)
 
 	guard(mutex)(&gpc->refresh_lock);
 
+	if (!kvm_gpc_is_valid_len(gpc->gpa, gpc->uhva, len))
+		return -EINVAL;
+
 	/*
 	 * If the GPA is valid then ignore the HVA, as a cache can be GPA-based
 	 * or HVA-based, not both.  For GPA-based caches, the HVA will be
@@ -372,7 +371,7 @@  int kvm_gpc_refresh(struct gfn_to_pfn_cache *gpc, unsigned long len)
 	 */
 	uhva = kvm_is_error_gpa(gpc->gpa) ? gpc->uhva : KVM_HVA_ERR_BAD;
 
-	return __kvm_gpc_refresh(gpc, gpc->gpa, uhva, len);
+	return __kvm_gpc_refresh(gpc, gpc->gpa, uhva);
 }
 
 void kvm_gpc_init(struct gfn_to_pfn_cache *gpc, struct kvm *kvm)
@@ -392,6 +391,9 @@  static int __kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned
 {
 	struct kvm *kvm = gpc->kvm;
 
+	if (!kvm_gpc_is_valid_len(gpa, uhva, len))
+		return -EINVAL;
+
 	guard(mutex)(&gpc->refresh_lock);
 
 	if (!gpc->active) {
@@ -411,7 +413,7 @@  static int __kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned
 		gpc->active = true;
 		write_unlock_irq(&gpc->lock);
 	}
-	return __kvm_gpc_refresh(gpc, gpa, uhva, len);
+	return __kvm_gpc_refresh(gpc, gpa, uhva);
 }
 
 int kvm_gpc_activate(struct gfn_to_pfn_cache *gpc, gpa_t gpa, unsigned long len)