>From: Paul Mackerras [mailto:pau...@ozlabs.org] Thursday, August 24, 2017 >11:40 AM > >Nixiaoming pointed out that there is a memory leak in >kvm_vm_ioctl_create_spapr_tce() if the call to anon_inode_getfd() fails; the >memory allocated for the kvmppc_spapr_tce_table struct is not freed, and nor >are the pages allocated for the iommu tables. In addition, we have already >incremented the process's count of locked memory pages, and this doesn't get >restored on error. > >David Hildenbrand pointed out that there is a race in that the function checks >early on that there is not already an entry in the >stt->iommu_tables list with the same LIOBN, but an entry with the >same LIOBN could get added between then and when the new entry is added to the >list. > >This fixes all three problems. To simplify things, we now call >anon_inode_getfd() before placing the new entry in the list. The check for an >existing entry is done while holding the kvm->lock mutex, immediately before >adding the new entry to the list. >Finally, on failure we now call kvmppc_account_memlimit to decrement the >process's count of locked memory pages. > >Reported-by: Nixiaoming <nixiaom...@huawei.com> >Reported-by: David Hildenbrand <da...@redhat.com> >Signed-off-by: Paul Mackerras <pau...@ozlabs.org> >--- > arch/powerpc/kvm/book3s_64_vio.c | 55 ++++++++++++++++++++++++---------------- > 1 file changed, 33 insertions(+), 22 deletions(-) > >diff --git a/arch/powerpc/kvm/book3s_64_vio.c >b/arch/powerpc/kvm/book3s_64_vio.c >index a160c14304eb..d463c1cd0d8d 100644 >--- a/arch/powerpc/kvm/book3s_64_vio.c >+++ b/arch/powerpc/kvm/book3s_64_vio.c >@@ -297,29 +297,22 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, > unsigned long npages, size; > int ret = -ENOMEM; > int i; >+ int fd = -1; > > if (!args->size) > return -EINVAL; > >- /* Check this LIOBN hasn't been previously allocated */ >- list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) { >- if (stt->liobn == args->liobn) >- return -EBUSY; >- } >- > size = _ALIGN_UP(args->size, PAGE_SIZE >> 3); > npages = kvmppc_tce_pages(size); > ret = kvmppc_account_memlimit(kvmppc_stt_pages(npages), true); >- if (ret) { >- stt = NULL; >- goto fail; >- } >+ if (ret) >+ return ret; > > ret = -ENOMEM; > stt = kzalloc(sizeof(*stt) + npages * sizeof(struct page *), > GFP_KERNEL); > if (!stt) >- goto fail; >+ goto fail_acct; > > stt->liobn = args->liobn; > stt->page_shift = args->page_shift; >@@ -334,24 +327,42 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, > goto fail; > } > >- kvm_get_kvm(kvm); >+ ret = fd = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, >+ stt, O_RDWR | O_CLOEXEC); >+ if (ret < 0) >+ goto fail; > > mutex_lock(&kvm->lock); >- list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables); >+ >+ /* Check this LIOBN hasn't been previously allocated */ >+ ret = 0; >+ list_for_each_entry(stt, &kvm->arch.spapr_tce_tables, list) {
I think stt can not be used here need a new value for list_for_each_entry >+ if (stt->liobn == args->liobn) { >+ ret = -EBUSY; >+ break; >+ } >+ } >+ >+ if (!ret) { >+ list_add_rcu(&stt->list, &kvm->arch.spapr_tce_tables); >+ kvm_get_kvm(kvm); >+ } > > mutex_unlock(&kvm->lock); > >- return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, >- stt, O_RDWR | O_CLOEXEC); >+ if (!ret) >+ return fd; > >-fail: >- if (stt) { >- for (i = 0; i < npages; i++) >- if (stt->pages[i]) >- __free_page(stt->pages[i]); >+ put_unused_fd(fd); > >- kfree(stt); >- } >+ fail: >+ for (i = 0; i < npages; i++) >+ if (stt->pages[i]) >+ __free_page(stt->pages[i]); >+ >+ kfree(stt); >+ fail_acct: >+ kvmppc_account_memlimit(kvmppc_stt_pages(npages), false); > return ret; > } > >-- >2.11.0 Thanks