From 528ce48167f96155df9591ed8846b31891bcf81e Mon Sep 17 00:00:00 2001 From: Charan Teja Kalla Date: Tue, 7 Mar 2023 07:22:26 +0530 Subject: [PATCH] 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 (cherry picked from commit 521f3bc70dc82a29f087883be2d77fd91399a843) --- mm/memory.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/mm/memory.c b/mm/memory.c index c617ca2dafe0..2bb47f9c34e5 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);