Thread (14 messages) 14 messages, 5 authors, 2017-08-28

Re: [PATCH] fix memory leak on kvm_vm_ioctl_create_spapr_tce

From: David Hildenbrand <hidden>
Date: 2017-08-23 08:25:49
Also in: kvm, linuxppc-dev

On 23.08.2017 08:06, Paul Mackerras wrote:
On Wed, Aug 23, 2017 at 01:43:08AM +0000, Nixiaoming wrote:
quoted
quoted
On 22.08.2017 17:15, David Hildenbrand wrote:
quoted
On 22.08.2017 16:28, nixiaoming wrote:
quoted
miss kfree(stt) when anon_inode_getfd return fail so add check 
anon_inode_getfd return val, and kfree stt

Signed-off-by: nixiaoming <redacted>
---
 arch/powerpc/kvm/book3s_64_vio.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/kvm/book3s_64_vio.c 
b/arch/powerpc/kvm/book3s_64_vio.c
index a160c14..a0b4459 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -341,8 +341,11 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm 
*kvm,
 
 	mutex_unlock(&kvm->lock);
 
-	return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
+	ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
 				stt, O_RDWR | O_CLOEXEC);
+	if (ret < 0)
+		goto fail;
+	return ret;
 
 fail:
 	if (stt) {

stt has already been added to kvm->arch.spapr_tce_tables, so freeing 
it is evil IMHO. I don't know that code, so I don't know if there is 
some other place that will make sure that everything in
kvm->arch.spapr_tce_tables will properly get freed, even when no 
kvm->release
function has been called (kvm_spapr_tce_release).
If it is really not freed, than also kvm_put_kvm(stt->kvm) is missing.

-- 

Thanks,

David
if (!stt) return -ENOMEM;
kvm_get_kvm(kvm);
if anon_inode_getfd return -ENOMEM
The user can not determine whether kvm_get_kvm has been called
so need add kvm_pet_kvm when anon_inode_getfd fail

stt has already been added to kvm->arch.spapr_tce_tables,
but if anon_inode_getfd fail, stt is unused val, 
so call list_del_rcu, and  free as quickly as possible

new patch:

---
 arch/powerpc/kvm/book3s_64_vio.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/arch/powerpc/kvm/book3s_64_vio.c b/arch/powerpc/kvm/book3s_64_vio.c
index a160c14..e2228f1 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -341,8 +341,16 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,

        mutex_unlock(&kvm->lock);

-       return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
+       ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
                                stt, O_RDWR | O_CLOEXEC);
+       if (ret < 0) {
+               mutex_lock(&kvm->lock);
+               list_del_rcu(&stt->list);
... don't we have to take care of rcu synchronization before freeing it?
quoted
+               mutex_unlock(&kvm->lock);
+               kvm_put_kvm(kvm);
+               goto fail;
+       }
+       return ret;
of simply

if (!ret)
	return 0;

mutex_lock(&kvm->lock);
list_del_rcu(&stt->list);
mutex_unlock(&kvm->lock);
kvm_put_kvm(kvm);

It seems to me that it would be better to do the anon_inode_getfd()
call before the kvm_get_kvm() call, and go to the fail label if it
fails.
I would have suggested to not add it to the list before it has been
fully created (so nobody can have access to it). But I guess than we
need another level of protection(e.g. kvm->lock).

Am I missing something, or is kvm_vm_ioctl_create_spapr_tce() racy?

The -EBUSY check is done without any locking, so two parallel creators
could create an inconsistency, no? Shouldn't this all be protected by
kvm->lock?
Paul.
Independent of the fix, I'd suggest the following cleanup.

From 979f55083ee965e25827a8743e8a9fdb85231a6f Mon Sep 17 00:00:00 2001
From: David Hildenbrand <redacted>
Date: Wed, 23 Aug 2017 10:08:58 +0200
Subject: [PATCH v1 1/1] KVM: PPC: cleanup kvm_vm_ioctl_create_spapr_tce

Let's simplify error handling.

Signed-off-by: David Hildenbrand <redacted>
---
 arch/powerpc/kvm/book3s_64_vio.c | 29 +++++++++++------------------
 1 file changed, 11 insertions(+), 18 deletions(-)
diff --git a/arch/powerpc/kvm/book3s_64_vio.c
b/arch/powerpc/kvm/book3s_64_vio.c
index a160c14304eb..6bac49292296 100644
--- a/arch/powerpc/kvm/book3s_64_vio.c
+++ b/arch/powerpc/kvm/book3s_64_vio.c
@@ -295,8 +295,7 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
 {
 	struct kvmppc_spapr_tce_table *stt = NULL;
 	unsigned long npages, size;
-	int ret = -ENOMEM;
-	int i;
+	int i, ret;

 	if (!args->size)
 		return -EINVAL;
@@ -310,16 +309,13 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
 	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;
+		return -ENOMEM;

 	stt->liobn = args->liobn;
 	stt->page_shift = args->page_shift;
@@ -331,7 +327,7 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
 	for (i = 0; i < npages; i++) {
 		stt->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
 		if (!stt->pages[i])
-			goto fail;
+			goto fail_free;
 	}

 	kvm_get_kvm(kvm);
@@ -344,15 +340,12 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,
 	return anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
 				stt, O_RDWR | O_CLOEXEC);

-fail:
-	if (stt) {
-		for (i = 0; i < npages; i++)
-			if (stt->pages[i])
-				__free_page(stt->pages[i]);
-
-		kfree(stt);
-	}
-	return ret;
+fail_free:
+	for (i = 0; i < npages; i++)
+		if (stt->pages[i])
+			__free_page(stt->pages[i]);
+	kfree(stt);
+	return -ENOMEM;
 }

 static void kvmppc_clear_tce(struct iommu_table *tbl, unsigned long entry)
-- 
2.13.5


-- 

Thanks,

David
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help