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 <quic_vjitta@quicinc.com>
This commit is contained in:
committed by
Todd Kjos
parent
6febc3942c
commit
385b0dd1f9
11
mm/memory.c
11
mm/memory.c
@@ -4806,6 +4806,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
|
||||
}
|
||||
|
||||
p4d = p4d_offset(pgd, address);
|
||||
if (pgd_val(READ_ONCE(*pgd)) != pgd_val(pgdval))
|
||||
goto spf_fail;
|
||||
p4dval = READ_ONCE(*p4d);
|
||||
if (p4d_none(p4dval) || unlikely(p4d_bad(p4dval))) {
|
||||
count_vm_spf_event(SPF_ABORT_PUD);
|
||||
@@ -4813,6 +4815,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
|
||||
}
|
||||
|
||||
vmf.pud = pud_offset(p4d, address);
|
||||
if (p4d_val(READ_ONCE(*p4d)) != p4d_val(p4dval))
|
||||
goto spf_fail;
|
||||
pudval = READ_ONCE(*vmf.pud);
|
||||
if (pud_none(pudval) || unlikely(pud_bad(pudval)) ||
|
||||
unlikely(pud_trans_huge(pudval)) ||
|
||||
@@ -4822,6 +4826,8 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
|
||||
}
|
||||
|
||||
vmf.pmd = pmd_offset(vmf.pud, address);
|
||||
if (pud_val(READ_ONCE(*vmf.pud)) != pud_val(pudval))
|
||||
goto spf_fail;
|
||||
vmf.orig_pmd = READ_ONCE(*vmf.pmd);
|
||||
|
||||
/*
|
||||
@@ -4853,6 +4859,11 @@ static vm_fault_t __handle_mm_fault(struct vm_area_struct *vma,
|
||||
*/
|
||||
|
||||
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 spf_fail;
|
||||
}
|
||||
vmf.orig_pte = READ_ONCE(*vmf.pte);
|
||||
barrier();
|
||||
if (pte_none(vmf.orig_pte)) {
|
||||
|
||||
Reference in New Issue
Block a user