Replace current->mm by kvm->mm on powerpc/kvm
Subsystems (2)
v2 cover Replace current->mm by kvm->mm on powerpc/kvm
By replacing, we would reduce the use of 'global' current on code, relying more in the contents of kvm struct.
On code, I found that in kvm_create_vm() there is:kvm->mm = current->mm;
And that on every kvm_*_ioctl we have tests like that:
if (kvm->mm != current->mm)
return -EIO;
So this change would be safe.
Also, I fixed a possible 'use after free' of kvm variable in kvm_vm_ioctl_create_spapr_tce, where it does a mutex_unlock(&kvm->lock) after a kvm_put_kvm(kvm).
Changes since v1:
- Fixes possible 'use after free' on kvm_spapr_tce_release (from v1)
- Fixes possible 'use after free' on kvm_vm_ioctl_create_spapr_tce
- Fixes undeclared variable error
Build test:
- https://travis-ci.org/LeoBras/linux-ppc/builds/608807573
Leonardo Bras (4):
powerpc/kvm/book3s: Fixes possible 'use after release' of kvm powerpc/kvm/book3s: Replace current->mm by kvm->mm powerpc/kvm/book3e: Replace current->mm by kvm->mm powerpc/kvm/e500: Replace current->mm by kvm->mm
arch/powerpc/kvm/book3s_64_mmu_hv.c | 10 +++++----- arch/powerpc/kvm/book3s_64_vio.c | 13 +++++++------ arch/powerpc/kvm/book3s_hv.c | 10 +++++----- arch/powerpc/kvm/booke.c | 2 +- arch/powerpc/kvm/e500_mmu_host.c | 6 +++--- 5 files changed, 21 insertions(+), 20 deletions(-)
Signature · 1 line
2.23.0
PATCH v2 1/4 powerpc/kvm/book3s: Fixes possible 'use after release' of kvm
Fixes a possible 'use after free' of kvm variable in kvm_vm_ioctl_create_spapr_tce, where it does a mutex_unlock(&kvm->lock) after a kvm_put_kvm(kvm).
--- arch/powerpc/kvm/book3s_64_vio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
arch/powerpc/kvm/book3s_64_vio.c +1 -2
@@ -316,14 +316,13 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,if (ret >=0 )list_add_rcu (&stt->list, &kvm->arch.spapr_tce_tables); -else -kvm_put_kvm (kvm);mutex_unlock (&kvm->lock);if (ret >=0 )return ret; +kvm_put_kvm (kvm);kfree (stt); fail_acct:account_locked_vm (current->mm,kvmppc_stt_pages (npages), false);
Signature · 1 line
2.23.0
Michael Ellerman
reviewerRe: [PATCH v2 1/4] powerpc/kvm/book3s: Fixes possible 'use after release' of kvm
Hi Leonardo,
Quoted 3 lines · Leonardo Bras
Fixes a possible 'use after free' of kvm variable in
kvm_vm_ioctl_create_spapr_tce, where it does amutex_unlock(&kvm->lock)after akvm_put_kvm(kvm).
There is no potential for an actual use after free here AFAICS.
arch/powerpc/kvm/book3s_64_vio.c
Quoted 4 lines
arch/powerpc/kvm/book3s_64_vio.c +0
-0
The preceeding context is:
mutex_lock(&kvm->lock);
/* Check this LIOBN hasn't been previously allocated */
ret = 0;
list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) {
if (siter->liobn = args->liobn) {
ret = -EBUSY;
break;
}
}
kvm_get_kvm(kvm);
if (!ret)
ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops,
stt, O_RDWR | O_CLOEXEC);Quoted 6 lines
arch/powerpc/kvm/book3s_64_vio.c +0
-2@@ -316,14 +316,13 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,if (ret >=0 )list_add_rcu (&stt->list, &kvm->arch.spapr_tce_tables); -else -kvm_put_kvm (kvm);
arch/powerpc/kvm/book3s_64_vio.c long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, · line 328
Quoted 10 lines
arch/powerpc/kvm/book3s_64_vio.c +1
-0mutex_unlock (&kvm->lock);if (ret >=0 )return ret; +kvm_put_kvm (kvm);kfree (stt); fail_acct:account_locked_vm (current->mm,kvmppc_stt_pages (npages), false);
If the kvm_put_kvm() you've moved actually caused the last reference to be dropped that would mean that our caller had passed us a kvm struct without holding a reference to it, and that would be a bug in our caller.
Or put another way, it would mean the mutex_lock() above could already be operating on a freed kvm struct.
The kvm_get_kvm() prior to the anon_inode_getfd() is to account for the reference that's held by the stt struct, and dropped in kvm_spapr_tce_release().
So although this patch isn't wrong, the explanation is not accurate.
cheers
Re: [PATCH v2 1/4] powerpc/kvm/book3s: Fixes possible 'use after release' of kvm
Quoted 1 line · Michael Ellerman
Hi Leonardo,
Hello Micheal, thanks for the feedback!
Quoted 47 lines
Quoted 4 lines · Leonardo Bras
Fixes a possible 'use after free' of kvm variable in
kvm_vm_ioctl_create_spapr_tce, where it does a mutex_unlock(&kvm-Quoted 1 line
lock)
after a
kvm_put_kvm(kvm).There is no potential for an actual use after free here AFAICS.
arch/powerpc/kvm/book3s_64_vio.c
Quoted 5 lines
arch/powerpc/kvm/book3s_64_vio.c +0
-0The preceeding context is:
mutex_lock(&kvm->lock); /* Check this LIOBN hasn't been previously allocated */ ret = 0; list_for_each_entry(siter, &kvm->arch.spapr_tce_tables, list) { if (siter->liobn == args->liobn) { ret = -EBUSY; break; } } kvm_get_kvm(kvm); if (!ret) ret = anon_inode_getfd("kvm-spapr-tce", &kvm_spapr_tce_fops, stt, O_RDWR | O_CLOEXEC);Quoted 13 lines
arch/powerpc/kvm/book3s_64_vio.c +0
-2@@ -316,14 +316,13 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, if (ret >= 0)list_add_rcu (&stt->list, &kvm->arch.spapr_tce_tables); - else -kvm_put_kvm (kvm );mutex_unlock (&kvm->lock);if (ret >=0 )return ret;
Quoted 12 lines
Quoted 5 lines
arch/powerpc/kvm/book3s_64_vio.c +1
-0+kvm_put_kvm (kvm);kfree (stt); fail_acct:account_locked_vm(current->mm,
kvmppc_stt_pages(npages),
false);If the
kvm_put_kvm()you've moved actually caused the last reference to
be dropped that would mean that our caller had passed us a kvm struct without holding a reference to it, and that would be a bug in our caller.
So, there is no chance that between this function's kvm_get_kvm() and kvm_put_kvm(), another thread can decrease this reference counter?
Quoted 4 lines
Or put another way, it would mean the
mutex_lock()above could already
be operating on a freed kvm struct.
Quoted 8 lines
The
kvm_get_kvm()prior to theanon_inode_getfd()is to account for the
reference that's held by thesttstruct, and dropped inkvm_spapr_tce_release().So although this patch isn't wrong, the explanation is not accurate.
cheers
Kind regards
Re: [PATCH v2 1/4] powerpc/kvm/book3s: Fixes possible 'use after release' of kvm
Quoted 9 lines · Leonardo Bras
Quoted 8 lines
If the
kvm_put_kvm()you've moved actually caused the last reference
to
be dropped that would mean that our caller had passed us a kvm struct
without holding a reference to it, and that would be a bug in our caller.
Quoted 3 lines
So, there is no chance that between this function's
kvm_get_kvm()andkvm_put_kvm(), another thread can decrease this reference counter?
I am probably missing something here, could you please help me understand that?
Quoted 9 lines
Quoted 9 lines
Or put another way, it would mean the
mutex_lock()above could already
be operating on a freed kvm struct.The
kvm_get_kvm()prior to theanon_inode_getfd()is to account for the
reference that's held by thesttstruct, and dropped inkvm_spapr_tce_release().
Quoted 6 lines
Quoted 4 lines
So although this patch isn't wrong, the explanation is not accurate.
cheers
Kind regards
Best regards,
PATCH v2 2/4 powerpc/kvm/book3s: Replace current->mm by kvm->mm
In reply to Leonardo BrasGiven that in kvm_create_vm() there is:kvm->mm = current->mm;
And that on every kvm_*_ioctl we have:
if (kvm->mm != current->mm)
return -EIO;
I see no reason to keep using current->mm instead of kvm->mm.
By doing so, we would reduce the use of 'global' variables on code, relying more in the contents of kvm struct.
--- arch/powerpc/kvm/book3s_64_mmu_hv.c | 10 +++++----- arch/powerpc/kvm/book3s_64_vio.c | 10 ++++++---- arch/powerpc/kvm/book3s_hv.c | 10 +++++----- 3 files changed, 16 insertions(+), 14 deletions(-)
arch/powerpc/kvm/book3s_64_mmu_hv.c +5 -5
@@ -296,7 +296,7 @@ static long kvmppc_virtmode_do_h_enter(struct kvm *kvm, unsigned long flags,/* Protect linux PTE lookup from page table destruction */ rcu_read_lock_sched ();/* this disables preemption too */ ret =kvmppc_do_h_enter (kvm, flags, pte_index, pteh, ptel, - current->mm->pgd, false, pte_idx_ret); + kvm->mm->pgd, false, pte_idx_ret);rcu_read_unlock_sched ();if (ret =H_TOO_HARD ) {/* this can't happen */
@@ -592,8 +592,8 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, npages =get_user_pages_fast (hva,1 , writing ?FOLL_WRITE :0 , pages);if (npages <1 ) {/* Check if it's an I/O mapping */ -down_read (¤t->mm->mmap_sem); - vma =find_vma (current->mm, hva); +down_read (&kvm->mm->mmap_sem); + vma =find_vma (kvm->mm, hva);if (vma && vma->vm_start <= hva && hva + psize <= vma->vm_end && (vma->vm_flags &VM_PFNMAP )) { pfn = vma->vm_pgoff +
@@ -602,7 +602,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu, is_ci =pte_ci (__pte ((pgprot_val (vma->vm_page_prot)))); write_ok = vma->vm_flags &VM_WRITE ; } -up_read (¤t->mm->mmap_sem); +up_read (&kvm->mm->mmap_sem);if (!pfn) goto out_put; }else {
@@ -621,7 +621,7 @@ int kvmppc_book3s_hv_page_fault(struct kvm_run *run, struct kvm_vcpu *vcpu,* hugepage split and collapse. */ local_irq_save (flags); - ptep =find_current_mm_pte (current->mm->pgd, + ptep =find_current_mm_pte (kvm->mm->pgd, hva,NULL ,NULL );if (ptep) { pte =kvmppc_read_update_linux_pte (ptep,1 );
arch/powerpc/kvm/book3s_64_vio.c +6 -4
@@ -253,10 +253,11 @@ static int kvm_spapr_tce_release(struct inode *inode, struct file *filp) } } +account_locked_vm (kvm->mm, +kvmppc_stt_pages (kvmppc_tce_pages (stt->size)), false); +kvm_put_kvm (stt->kvm); -account_locked_vm (current->mm, -kvmppc_stt_pages (kvmppc_tce_pages (stt->size)), false);call_rcu (&stt->rcu, release_spapr_tce_table);return 0 ;
@@ -272,6 +273,7 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm, {struct kvmppc_spapr_tce_table *stt =NULL ;struct kvmppc_spapr_tce_table *siter; +struct mm_struct *mm = kvm->mm;unsigned long npages, size = args->size;int ret = -ENOMEM ;
@@ -280,7 +282,7 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,return -EINVAL ; npages =kvmppc_tce_pages (size); - ret =account_locked_vm (current->mm,kvmppc_stt_pages (npages), true); + ret =account_locked_vm (mm,kvmppc_stt_pages (npages), true);if (ret)return ret;
@@ -325,7 +327,7 @@ long kvm_vm_ioctl_create_spapr_tce(struct kvm *kvm,kvm_put_kvm (kvm);kfree (stt); fail_acct: -account_locked_vm (current->mm,kvmppc_stt_pages (npages), false); +account_locked_vm (mm,kvmppc_stt_pages (npages), false);return ret; }
arch/powerpc/kvm/book3s_hv.c +5 -5
@@ -4280,7 +4280,7 @@ static int kvmppc_vcpu_run_hv(struct kvm_run *run, struct kvm_vcpu *vcpu) user_vrsave =mfspr (SPRN_VRSAVE ); vcpu->arch.wqp = &vcpu->arch.vcore->wq; - vcpu->arch.pgdir = current->mm->pgd; + vcpu->arch.pgdir = kvm->mm->pgd; vcpu->arch.state =KVMPPC_VCPU_BUSY_IN_HOST ; do {
@@ -4612,14 +4612,14 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)/* Look up the VMA for the start of this memory slot */ hva = memslot->userspace_addr; -down_read (¤t->mm->mmap_sem); - vma =find_vma (current->mm, hva); +down_read (&kvm->mm->mmap_sem); + vma =find_vma (kvm->mm, hva);if (!vma || vma->vm_start > hva || (vma->vm_flags &VM_IO )) goto up_out; psize =vma_kernel_pagesize (vma); -up_read (¤t->mm->mmap_sem); +up_read (&kvm->mm->mmap_sem);/* We can handle 4k, 64k or 16M pages in the VRMA */ if (psize >=0x1000000 )
@@ -4652,7 +4652,7 @@ static int kvmppc_hv_setup_htab_rma(struct kvm_vcpu *vcpu)return err; up_out: -up_read (¤t->mm->mmap_sem); +up_read (&kvm->mm->mmap_sem); goto out_srcu; }
Signature · 1 line
2.23.0
PATCH v2 3/4 powerpc/kvm/book3e: Replace current->mm by kvm->mm
In reply to Leonardo BrasGiven that in kvm_create_vm() there is:kvm->mm = current->mm;
And that on every kvm_*_ioctl we have:
if (kvm->mm != current->mm)
return -EIO;
I see no reason to keep using current->mm instead of kvm->mm.
By doing so, we would reduce the use of 'global' variables on code, relying more in the contents of kvm struct.
--- arch/powerpc/kvm/booke.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
arch/powerpc/kvm/booke.c +1 -1
@@ -775,7 +775,7 @@ int kvmppc_vcpu_run(struct kvm_run *kvm_run, struct kvm_vcpu *vcpu) debug = current->thread.debug; current->thread.debug = vcpu->arch.dbg_reg; - vcpu->arch.pgdir = current->mm->pgd; + vcpu->arch.pgdir = vcpu->kvm->mm->pgd;kvmppc_fix_ee_before_entry (); ret =__kvmppc_vcpu_run (kvm_run, vcpu);
Signature · 1 line
2.23.0
PATCH v2 4/4 powerpc/kvm/e500: Replace current->mm by kvm->mm
In reply to Leonardo BrasGiven that in kvm_create_vm() there is:kvm->mm = current->mm;
And that on every kvm_*_ioctl we have:
if (kvm->mm != current->mm)
return -EIO;
I see no reason to keep using current->mm instead of kvm->mm.
By doing so, we would reduce the use of 'global' variables on code, relying more in the contents of kvm struct.
--- arch/powerpc/kvm/e500_mmu_host.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-)
arch/powerpc/kvm/e500_mmu_host.c +3 -3
@@ -355,9 +355,9 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500,if (tlbsel =1 ) {struct vm_area_struct *vma; -down_read (¤t->mm->mmap_sem); +down_read (&kvm->mm->mmap_sem); - vma =find_vma (current->mm, hva); + vma =find_vma (kvm->mm, hva);if (vma && hva >= vma->vm_start && (vma->vm_flags &VM_PFNMAP )) {/*
@@ -441,7 +441,7 @@ static inline int kvmppc_e500_shadow_map(struct kvmppc_vcpu_e500 *vcpu_e500, tsize =max (BOOK3E_PAGESZ_4K , tsize & ~1 ); } -up_read (¤t->mm->mmap_sem); +up_read (&kvm->mm->mmap_sem); }if (likely (!pfnmap)) {
Signature · 1 line
2.23.0