ANDROID: mm: fix UAF in SPF
The below race is observed on the SPF path:
pthread1 pthread2
-------- --------
Speculatively enter
__pte_map_lock:
irq_disable()
............
All seq lock checks
are succeeded.
............
ptl = (pmd_page(*pmd))->ptl
__vm_munmap():
mmap_write_lock_killable();
(update the seq count)
__do_munmap()-->
unmap_region()-->
free_pgtables()-->
..........
free_pte_range():
ptl = pmd_lock()
unlock(ptl)
pte_free_tlb()-->
.........
pgtable_pte_page_dtor():
(Free the pmd_page(page)->ptl
to the slab, on which pthread1
still operating on)
spin_trylock(ptl)
Seq count check fails
spin_unlock():
SPIN_BUG() checks are passed
kmem_cache_free()-->
do_slab_free():
(a) *(ptl + offset) = c->freelist
(b) c->freelist = ptl
update ptl->owner, owner->cpu
(This is use-after-free of
the ptl slab object which is
corrupting the next pointer,(a),
filled by the pthread2 in the
cuuren->freelist)
Note that when DEBUG_SPINLOCK is not enabled, race won't exist as
ALLOC_SPLIT_PTLOCKS is not defined i.e. ptl is stored directly in the
page structure. Note that this change as uses smp sync doesn't have any
perf impact in the production builds as the intended code is under
ALLOC_SPLIT_PTLOCKS which will be absent.
Bug: 265837312
Change-Id: I05b11c80a45c285ab8d293cca925aa4388f62d05
Signed-off-by: Charan Teja Kalla <quic_charante@quicinc.com>
(cherry picked from commit 521f3bc70dc82a29f087883be2d77fd91399a843)
diff --git a/mm/memory.c b/mm/memory.c
index c617ca2..2bb47f9 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -253,6 +253,11 @@ void put_vma(struct vm_area_struct *vma)
vm_area_free_no_check(vma);
}
+#if ALLOC_SPLIT_PTLOCKS
+static void wait_for_smp_sync(void *arg)
+{
+}
+#endif
#endif /* CONFIG_SPECULATIVE_PAGE_FAULT */
/*
@@ -272,6 +277,14 @@ static void free_pte_range(struct mmu_gather *tlb, pmd_t *pmd,
*/
spinlock_t *ptl = pmd_lock(tlb->mm, pmd);
spin_unlock(ptl);
+#if ALLOC_SPLIT_PTLOCKS
+ /*
+ * The __pte_map_lock can still be working on the ->ptl in the read side
+ * critical section while ->ptl is freed which results into the use-after
+ * -free. Sync it using the smp_call_().
+ */
+ smp_call_function(wait_for_smp_sync, NULL, 1);
+#endif
#endif
pmd_clear(pmd);
pte_free_tlb(tlb, token, addr);