Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > media > main-src > by-pkgid > aadbe78a25743146bb784eee19f007c5 > files > 417

kvm-83-164.el5_5.9.src.rpm

From c73849baf5e6727b0ed235f8819b08e90c4a58c1 Mon Sep 17 00:00:00 2001
From: Marcelo Tosatti <mtosatti@redhat.com>
Date: Thu, 2 Jul 2009 17:53:41 -0300
Subject: [PATCH] MMU: limit rmap chain

Otherwise its possible to DoS the host by creating a very large number
of entries in the chain, whose access happens under a spinlock.

Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com>
Bugzilla: 508776
Message-ID: <20090702205341.GA19631@amt.cnet>
Signed-off-by: Eduardo Habkost <ehabkost@redhat.com>
Obsoletes: <20090629195442.GA19652@amt.cnet>
Obsoletes: <20090623214902.490499000@virtlab11.virt.bos.redhat.com>
Obsoletes: <20090630184510.GA31248@amt.cnet>
Acked-by: Glauber Costa <glommer@redhat.com>
Acked-by: Juan Quintela <quintela@redhat.com>
Acked-by: Gleb Natapov <gleb@redhat.com>
Acked-by: john cooper <john.cooper@redhat.com>
---
 arch/x86/kvm/mmu.c  |   36 +++++++++++++++++++++++++++++++-----
 include/linux/kvm.h |    1 +
 2 files changed, 32 insertions(+), 5 deletions(-)

diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c
index d82f9ab..6c1d15f 100644
--- a/arch/x86/kvm/mmu.c
+++ b/arch/x86/kvm/mmu.c
@@ -482,16 +482,20 @@ static unsigned long *gfn_to_rmap(struct kvm *kvm, gfn_t gfn, int lpage)
  *
  * If rmapp bit zero is one, (then rmap & ~1) points to a struct kvm_rmap_desc
  * containing more mappings.
+ * 
+ * Returns the number of rmap entries before the spte was added or zero if
+ * the spte was not added. 
  */
-static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
+static int rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
 {
 	struct kvm_mmu_page *sp;
 	struct kvm_rmap_desc *desc;
 	unsigned long *rmapp;
-	int i;
+	int i, count = 0;
 
 	if (!is_rmap_pte(*spte))
-		return;
+		return count;
+
 	gfn = unalias_gfn(vcpu->kvm, gfn);
 	sp = page_header(__pa(spte));
 	sp->gfns[spte - sp->spt] = gfn;
@@ -508,8 +512,10 @@ static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
 	} else {
 		rmap_printk("rmap_add: %p %llx many->many\n", spte, *spte);
 		desc = (struct kvm_rmap_desc *)(*rmapp & ~1ul);
-		while (desc->shadow_ptes[RMAP_EXT-1] && desc->more)
+		while (desc->shadow_ptes[RMAP_EXT-1] && desc->more) {
 			desc = desc->more;
+			count += RMAP_EXT;
+		}
 		if (desc->shadow_ptes[RMAP_EXT-1]) {
 			desc->more = mmu_alloc_rmap_desc(vcpu);
 			desc = desc->more;
@@ -518,6 +524,7 @@ static void rmap_add(struct kvm_vcpu *vcpu, u64 *spte, gfn_t gfn, int lpage)
 			;
 		desc->shadow_ptes[i] = spte;
 	}
+	return count;
 }
 
 static void rmap_desc_remove_entry(unsigned long *rmapp,
@@ -822,6 +829,22 @@ int kvm_age_hva(struct kvm *kvm, unsigned long hva)
 	return kvm_handle_hva(kvm, hva, kvm_age_rmapp, NULL);
 }
 
+#define RMAP_RECYCLE_THRESHOLD 1000
+
+static void rmap_recycle(struct kvm_vcpu *vcpu, gfn_t gfn, int lpage)
+{
+	unsigned long *rmapp;
+
+	gfn = unalias_gfn(vcpu->kvm, gfn);
+	rmapp = gfn_to_rmap(vcpu->kvm, gfn, lpage);
+
+	kvm_unmap_rmapp(vcpu->kvm, rmapp, NULL);
+	kvm_flush_remote_tlbs(vcpu->kvm);
+
+	KVMTRACE_3D(RMAP_NUKE, vcpu, (unsigned int)gfn, lpage,
+		    vcpu->kvm->stat.mmu_recycled, handler);
+}
+
 #ifdef MMU_DEBUG
 static int is_empty_shadow_page(u64 *spt)
 {
@@ -1826,6 +1849,7 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
 {
 	int was_rmapped = 0;
 	int was_writeble = is_writeble_pte(*shadow_pte);
+	int rmap_count;
 
 	pgprintk("%s: spte %llx access %x write_fault %d"
 		 " user_fault %d gfn %lx\n",
@@ -1871,9 +1895,11 @@ static void mmu_set_spte(struct kvm_vcpu *vcpu, u64 *shadow_pte,
 
 	page_header_update_slot(vcpu->kvm, shadow_pte, gfn);
 	if (!was_rmapped) {
-		rmap_add(vcpu, shadow_pte, gfn, largepage);
+		rmap_count = rmap_add(vcpu, shadow_pte, gfn, largepage);
 		if (!is_rmap_pte(*shadow_pte))
 			kvm_release_pfn_clean(pfn);
+		if (rmap_count > RMAP_RECYCLE_THRESHOLD)
+			rmap_recycle(vcpu, gfn, largepage);
 	} else {
 		if (was_writeble)
 			kvm_release_pfn_dirty(pfn);
diff --git a/include/linux/kvm.h b/include/linux/kvm.h
index c02654b..8367c15 100644
--- a/include/linux/kvm.h
+++ b/include/linux/kvm.h
@@ -564,6 +564,7 @@ struct kvm_irq_routing {
 #define KVM_TRC_STLB_WRITE       (KVM_TRC_HANDLER + 0x17)
 #define KVM_TRC_STLB_INVAL       (KVM_TRC_HANDLER + 0x18)
 #define KVM_TRC_PPC_INSTR        (KVM_TRC_HANDLER + 0x19)
+#define KVM_TRC_RMAP_NUKE        (KVM_TRC_HANDLER + 0x20)
 
 #define KVM_DEV_ASSIGN_ENABLE_IOMMU	(1 << 0)
 
-- 
1.6.3.rc4.29.g8146