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