From 1b3c72b43c03ef66265e94434869089d34345f77 Mon Sep 17 00:00:00 2001 From: Vijayanand Jitta Date: Wed, 2 Mar 2022 22:25:21 +0530 Subject: [PATCH] ANDROID: mm: Fix page table lookup in speculative fault path In speculative fault path, while doing page table lookup, offset is obtained at each level and value at that offset is read and checks are perfomed on it, later to get next level offset we read from previous level offset again. A concurrent page table reclaimation operation could result in change in value at this offset, and we go ahead and access it, this would result in reading an invalid entry. Fix this by reading from previous level offset again and comparing before performing next level access. Bug: 221005439 Change-Id: I66b3d24ae79c7ee5ccce4ba7a94f028f4cf3fda0 Signed-off-by: Vijayanand Jitta Signed-off-by: Srinivasarao Pathipati --- mm/memory.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/memory.c b/mm/memory.c index ae9a9821a8b4..c56b341f608c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -4458,11 +4458,15 @@ int __handle_speculative_fault(struct mm_struct *mm, unsigned long address, goto out_walk; p4d = p4d_offset(pgd, address); + if (pgd_val(READ_ONCE(*pgd)) != pgd_val(pgdval)) + goto out_walk; p4dval = READ_ONCE(*p4d); if (p4d_none(p4dval) || unlikely(p4d_bad(p4dval))) goto out_walk; vmf.pud = pud_offset(p4d, address); + if (p4d_val(READ_ONCE(*p4d)) != p4d_val(p4dval)) + goto out_walk; pudval = READ_ONCE(*vmf.pud); if (pud_none(pudval) || unlikely(pud_bad(pudval))) goto out_walk; @@ -4472,6 +4476,8 @@ int __handle_speculative_fault(struct mm_struct *mm, unsigned long address, goto out_walk; vmf.pmd = pmd_offset(vmf.pud, address); + if (pud_val(READ_ONCE(*vmf.pud)) != pud_val(pudval)) + goto out_walk; vmf.orig_pmd = READ_ONCE(*vmf.pmd); /* * pmd_none could mean that a hugepage collapse is in progress @@ -4499,6 +4505,11 @@ int __handle_speculative_fault(struct mm_struct *mm, unsigned long address, */ vmf.pte = pte_offset_map(vmf.pmd, address); + if (pmd_val(READ_ONCE(*vmf.pmd)) != pmd_val(vmf.orig_pmd)) { + pte_unmap(vmf.pte); + vmf.pte = NULL; + goto out_walk; + } vmf.orig_pte = READ_ONCE(*vmf.pte); barrier(); /* See comment in handle_pte_fault() */ if (pte_none(vmf.orig_pte)) {