Message ID | 4F9ECAEB.1050706@canonical.com |
---|---|
State | New |
Headers | show |
On 30.04.2012 19:24, Steve Conklin wrote: > This is a backport from upstream. There are changes due to a change that > was made in parameters passed to a function, but it's straightforward > change. > > From 2739eaeac4a9bf777810b2837e528dbdbdb3d5cc Mon Sep 17 00:00:00 2001 > From: Alex Williamson <alex.williamson@redhat.com> > Date: Wed, 11 Apr 2012 09:51:49 -0600 > Subject: [PATCH] KVM: unmap pages from the iommu when slots are removed > > CVE-2012-2121 > > BugLink: http://bugs.launchpad.net/bugs/987569 > > We've been adding new mappings, but not destroying old mappings. > This can lead to a page leak as pages are pinned using > get_user_pages, but only unpinned with put_page if they still > exist in the memslots list on vm shutdown. A memslot that is > destroyed while an iommu domain is enabled for the guest will > therefore result in an elevated page reference count that is > never cleared. > > Additionally, without this fix, the iommu is only programmed > with the first translation for a gpa. This can result in > peer-to-peer errors if a mapping is destroyed and replaced by a > new mapping at the same gpa as the iommu will still be pointing > to the original, pinned memory address. > > Signed-off-by: Alex Williamson <alex.williamson@redhat.com> > Signed-off-by: Marcelo Tosatti <mtosatti@redhat.com> > > Backported with minor changes from upstream commit > 32f6daad4651a748a58a3ab6da0611862175722f > Signed-off-by: Steve Conklin <sconklin@canonical.com> > --- > include/linux/kvm_host.h | 6 ++++++ > virt/kvm/iommu.c | 8 ++++++-- > virt/kvm/kvm_main.c | 5 +++-- > 3 files changed, 15 insertions(+), 4 deletions(-) > > diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h > index 7e8a29f..6136821 100644 > --- a/include/linux/kvm_host.h > +++ b/include/linux/kvm_host.h > @@ -562,6 +562,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int > irq_source_id); > > #ifdef CONFIG_IOMMU_API > int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot); > +void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot); > int kvm_iommu_map_guest(struct kvm *kvm); > int kvm_iommu_unmap_guest(struct kvm *kvm); > int kvm_assign_device(struct kvm *kvm, > @@ -575,6 +576,11 @@ static inline int kvm_iommu_map_pages(struct kvm *kvm, > return 0; > } > > +static inline void kvm_iommu_unmap_pages(struct kvm *kvm, > + struct kvm_memory_slot *slot) > +{ > +} > + > static inline int kvm_iommu_map_guest(struct kvm *kvm) > { > return -ENODEV; > diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c > index a195c07..981f55a 100644 > --- a/virt/kvm/iommu.c > +++ b/virt/kvm/iommu.c > @@ -309,6 +309,11 @@ static void kvm_iommu_put_pages(struct kvm *kvm, > } > } > > +void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot) > +{ > + kvm_iommu_put_pages(kvm, slot->base_gfn, slot->npages); > +} > + > static int kvm_iommu_unmap_memslots(struct kvm *kvm) > { > int i, idx; > @@ -318,8 +323,7 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm) > slots = kvm_memslots(kvm); > > for (i = 0; i < slots->nmemslots; i++) { > - kvm_iommu_put_pages(kvm, slots->memslots[i].base_gfn, > - slots->memslots[i].npages); > + kvm_iommu_unmap_pages(kvm, &slots->memslots[i]); > } > srcu_read_unlock(&kvm->srcu, idx); > > diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c > index 5007b7d..63e01de 100644 > --- a/virt/kvm/kvm_main.c > +++ b/virt/kvm/kvm_main.c > @@ -802,12 +802,13 @@ skip_lpage: > if (r) > goto out_free; > > - /* map the pages in iommu page table */ > + /* map/unmap the pages in iommu page table */ > if (npages) { > r = kvm_iommu_map_pages(kvm, &new); > if (r) > goto out_free; > - } > + } else > + kvm_iommu_unmap_pages(kvm, &old); > > r = -ENOMEM; > slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);
My email client thinks this patch got line wrapped, so I just cherry-picked it and fixed the conflict. Are you using 'git send-email' ? rtg
diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 7e8a29f..6136821 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -562,6 +562,7 @@ void kvm_free_irq_source_id(struct kvm *kvm, int irq_source_id); #ifdef CONFIG_IOMMU_API int kvm_iommu_map_pages(struct kvm *kvm, struct kvm_memory_slot *slot); +void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot); int kvm_iommu_map_guest(struct kvm *kvm); int kvm_iommu_unmap_guest(struct kvm *kvm); int kvm_assign_device(struct kvm *kvm, @@ -575,6 +576,11 @@ static inline int kvm_iommu_map_pages(struct kvm *kvm, return 0; } +static inline void kvm_iommu_unmap_pages(struct kvm *kvm, + struct kvm_memory_slot *slot) +{ +} + static inline int kvm_iommu_map_guest(struct kvm *kvm) { return -ENODEV; diff --git a/virt/kvm/iommu.c b/virt/kvm/iommu.c index a195c07..981f55a 100644 --- a/virt/kvm/iommu.c +++ b/virt/kvm/iommu.c @@ -309,6 +309,11 @@ static void kvm_iommu_put_pages(struct kvm *kvm, } } +void kvm_iommu_unmap_pages(struct kvm *kvm, struct kvm_memory_slot *slot) +{ + kvm_iommu_put_pages(kvm, slot->base_gfn, slot->npages); +} + static int kvm_iommu_unmap_memslots(struct kvm *kvm) { int i, idx; @@ -318,8 +323,7 @@ static int kvm_iommu_unmap_memslots(struct kvm *kvm) slots = kvm_memslots(kvm); for (i = 0; i < slots->nmemslots; i++) { - kvm_iommu_put_pages(kvm, slots->memslots[i].base_gfn, - slots->memslots[i].npages); + kvm_iommu_unmap_pages(kvm, &slots->memslots[i]); } srcu_read_unlock(&kvm->srcu, idx); diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 5007b7d..63e01de 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -802,12 +802,13 @@ skip_lpage: if (r) goto out_free; - /* map the pages in iommu page table */ + /* map/unmap the pages in iommu page table */ if (npages) { r = kvm_iommu_map_pages(kvm, &new); if (r) goto out_free; - } + } else + kvm_iommu_unmap_pages(kvm, &old); r = -ENOMEM; slots = kzalloc(sizeof(struct kvm_memslots), GFP_KERNEL);