Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5e8f99df authored by Jeff Boody's avatar Jeff Boody
Browse files

msm: kgsl: prevent kgsl_get_pagetable from returning a destroyed pt



A race condition exists between kref_get_pagetable and
kref_put_pagetable such that is is possible to put the last
reference to a pagetable slightly before it has been removed
from the list. By checking kref_get_unless_zero we can determine
if the last reference has already been released. This is safe
because the list is protected by a ptlock.

Change-Id: If6a75e9ca0dec6a12bda5739f835c936a4c3a3b4
Signed-off-by: default avatarJeff Boody <jboody@codeaurora.org>
parent 61a09d4a
Loading
Loading
Loading
Loading
+29 −16
Original line number Diff line number Diff line
@@ -124,11 +124,13 @@ kgsl_get_pagetable(unsigned long name)

	spin_lock_irqsave(&kgsl_driver.ptlock, flags);
	list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
		if (kref_get_unless_zero(&pt->refcount)) {
			if (pt->name == name) {
				ret = pt;
			kref_get(&ret->refcount);
				break;
			}
			kref_put(&pt->refcount, kgsl_destroy_pagetable);
		}
	}

	spin_unlock_irqrestore(&kgsl_driver.ptlock, flags);
@@ -324,10 +326,14 @@ kgsl_mmu_get_ptname_from_ptbase(struct kgsl_mmu *mmu, phys_addr_t pt_base)
		return KGSL_MMU_GLOBAL_PT;
	spin_lock(&kgsl_driver.ptlock);
	list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
		if (kref_get_unless_zero(&pt->refcount)) {
			if (mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base)) {
				ptid = (int) pt->name;
				kref_put(&pt->refcount, kgsl_destroy_pagetable);
				break;
			}
			kref_put(&pt->refcount, kgsl_destroy_pagetable);
		}
	}
	spin_unlock(&kgsl_driver.ptlock);

@@ -346,16 +352,23 @@ kgsl_mmu_log_fault_addr(struct kgsl_mmu *mmu, phys_addr_t pt_base,
		return 0;
	spin_lock(&kgsl_driver.ptlock);
	list_for_each_entry(pt, &kgsl_driver.pagetable_list, list) {
		if (kref_get_unless_zero(&pt->refcount)) {
			if (mmu->mmu_ops->mmu_pt_equal(mmu, pt, pt_base)) {
				if ((addr & ~(PAGE_SIZE-1)) == pt->fault_addr) {
					ret = 1;
					kref_put(&pt->refcount,
						kgsl_destroy_pagetable);
					break;
				} else {
				pt->fault_addr = (addr & ~(PAGE_SIZE-1));
					pt->fault_addr =
						(addr & ~(PAGE_SIZE-1));
					ret = 0;
					kref_put(&pt->refcount,
						kgsl_destroy_pagetable);
					break;
				}

			}
			kref_put(&pt->refcount, kgsl_destroy_pagetable);
		}
	}
	spin_unlock(&kgsl_driver.ptlock);